WordPress

最終更新日:
公開日:

レシピ

wpdbクラスのプリペアドステートメントについて

WordPressでデータベースの操作を行うときに、SQLインジェクション対策として有効なプリペアドステートメントを使う方法について解説します。

この記事のポイント

  • wpdbクラスのプリペアドステートメントはprepareメソッドを使う
  • データを登録するinsertメソッドは内部でprepareメソッドによるプリペアドステートメントを実行する
  • updateメソッドdeleteメソッドreplaceメソッドも同様に内部でプリペアドステートメントを実行する

wpdbクラスでプリペアドステートメントを使用する

WordPressでデータベースの操作を行うインターフェースを提供するwpdbクラスでは、SQLインジェクション対策になるプリペアドステートメントを使用するためのprepareメソッドが用意されています。
prepareメソッドを通すと値を自動的にエスケープしてくれるため、渡したSQLを安全に実行することができます。

index.php

<?php

global $wpdb;
$id = 10;
$res = null;

// データを検索
$res = $wpdb->get_row(
	$wpdb->prepare("SELECT * FROM table_name WHERE id = %d", $id)
, ARRAY_A);

データを登録するときにprepareメソッドを使う例は以下のようになります。
insertメソッドを使う方法は後ほど解説します)

index.php

<?php

global $wpdb;
$name = 'Taro';
$created = '2022-04-05 14:09:08';

// データを登録
$wpdb->query(
	$wpdb->prepare("INSERT INTO table_name
		( name, created)
		VALUES ( %s, %s)",
		$name,
		$created
 	)
 );

prepareメソッドは第1パラメータにSQL、第2パラメータ以降にプレースホルダーにセットする値を先頭から順に指定します。
第2パラメータ以降の値の数は第1パラメータのプレースホルダーの数と等しくなります。

第1パラメータのSQLに設置するプレースホルダーは文字列が入るときは%s、数値は%d、浮動小数点数は%fになります。

上記の例ではデータの登録であえてprepareメソッドを使っていますが、wpdbクラスではデータの登録ではinsertメソッドを使う方法もあります。
insertメソッドの内部でもprepareメソッドを実行してからqueryメソッドを実行しているため、やっていることは上記のコードと同じです。
プリペアドステートメントを手動で書くよりもミスが減るため、insertメソッドを使う方法の方がより安全と言えるかもしれません。

index.php

<?php

global $wpdb;
$name = 'Taro';
$created = '2022-04-05 14:09:08';

// データを登録
$wpdb->insert(
	'table_name',
	array(
		'name' => $name,
		'created' => $created
	),
	array(
		'%s',
		'%s'
	)
);

より厳密には、insertメソッドwp-includes/wp-db.phpの中で以下のように実装されています。

wp-includes/wp-db.php

<?php
/**
 * WordPress database access abstraction class
 *
 * Original code from {@link http://php.justinvincent.com Justin Vincent (justin@visunet.ie)}
 *
 * @package WordPress
 * @subpackage Database
 * @since 0.71
 */

---- 省略 -----

public function insert( $table, $data, $format = null ) {
	return $this->_insert_replace_helper( $table, $data, $format, 'INSERT' );
}

---- 省略 -----

public function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) {
	$this->insert_id = 0;

	if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ), true ) ) {
		return false;
	}

	$data = $this->process_fields( $table, $data, $format );
	if ( false === $data ) {
		return false;
	}

	$formats = array();
	$values  = array();
	foreach ( $data as $value ) {
		if ( is_null( $value['value'] ) ) {
			$formats[] = 'NULL';
			continue;
		}

		$formats[] = $value['format'];
		$values[]  = $value['value'];
	}

	$fields  = '`' . implode( '`, `', array_keys( $data ) ) . '`';
	$formats = implode( ', ', $formats );

	$sql = "$type INTO `$table` ($fields) VALUES ($formats)";

	$this->check_current_query = false;
	return $this->query( $this->prepare( $sql, $values ) );
}

上記のようにinsertメソッド_insert_replace_helperメソッドを呼び出し、_insert_replace_helperメソッドでは最後のreturnの部分でprepareメソッドqueryメソッドを実行していることが分かります。
特定のカラムのデータを更新するupdateメソッド、削除するdeleteメソッド、置き換えるreplaceメソッドも同様に内部でprepareメソッドqueryメソッドを実行するため、手動でプリペアドステートメントを実行する必要はありません。

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

コメントありがとうございます!
運営の参考にさせていただきます。

前のページへ 一覧に戻る 次のページへ