wpdb::prepare()
Позволяет писать SQL запрос с очисткой передаваемых в него параметров.
В строке запроса вместо передаваемого параметра нужно использовать плейсхолдер:
%d
(integer)%f
(float)%s
(string)
Также для каждого из плейсхолдеров указывается PHP переменная, которая заменит плейсхолдер. При замене переменная будет очищена. Синтаксис похожий на sprintf().
С WP 3.5 обязательно должны быть переданы минимум 2 параметра: запрос и значение переменной, иначе будет php ошибка (User Notice).
- Кавычки для плейсхолдеров
%s
и'%s'
. Плейсхолдеры могут быть в кавычках или без них:
WHERE field = %s
илиWHERE field = '%s'
. Кавычки принято не ставить.echo $wpdb->prepare( "foo = %s", 'a' ); // foo = 'a' echo $wpdb->prepare( "foo = '%s'", 'a' ); // foo = 'a'
- Параметр для каждого плейсхолдера.
Параметр должен быть указан для каждого плейсхолдера.
echo $wpdb->prepare( 'foo = %s AND bar = %s', 'a' ); echo $wpdb->prepare( 'foo = %1$s AND bar = %1$s', 'a' ); // в обоих случаях увидим ошибку: // User Notice: wpdb::prepare was called incorrectly. // The query does not contain the correct number of placeholders (2) // for the number of arguments passed (1).
- Порядковые плейсхолдеры
%1$s
. Для совместимости со старыми версиями: порядковые плейсхолдеры (например,
%1$s
,%5s
) обрабатываются иначе - им не добавляются кавычки, поэтому они должны быть снабжены правильными кавычками в строке запроса.echo $wpdb->prepare( 'foo = %1$s', 'a"a' ); // foo = a\"a echo $wpdb->prepare( 'foo = "%1$s"', 'a"a' ); // foo = "a\"a" echo $wpdb->prepare( 'foo = %1s', 'a"a' ); // foo = a\"a echo $wpdb->prepare( 'foo = %s', 'a"a' ); // foo = 'a\"a'
- Знак
%
Знак
%
в строке запроса, который не относится к плейсхолдеру нужно записывать так%%
.echo $wpdb->prepare( "%foo AND id = %d", 2 ); // User Notice: wpdb::prepare was called incorrectly. echo $wpdb->prepare( "%%foo AND id = %d", 2 ); // %foo AND id = 2
%
в LIKE синтаксисеПодстановочные знаки процента
%
в LIKE синтаксисе должны указываться через параметр подстановки содержащий полную LIKE строку, а не напрямую в запросе. Также смотрите wpdb::esc_like().$like = '%'. $wpdb->esc_like( "bar's" ) .'%end'; echo $wpdb->prepare( "foo LIKE %s", $like ); // foo LIKE '{a0d1d}bar\'s{a0d1d}end'
SQL инъекция
В SQL есть такое понятие как «инъекция» (внедрение в запрос SQL кода). Cделать его можно, когда в запрос передаются динамические данные. Например, в запрос передаётся значение input поля, в это поле можно указать данные, которые в итоге станут частью SQL запроса. Так можно внедриться в запрос и что-нибудь испортить или просто нарушить код самого запроса. Выглядит это так:
$sql = "SELECT * FROM table WHERE id = '$var'";
Теперь, если var = 2' AND id = (DROP TABLE table2)
то в результате запрос получиться такой:
SELECT * FROM table WHERE id = '2' AND id = (DROP TABLE table2)
Таким образом можно внедриться в сам запрос и изменить его. Чтобы этого не произошло запросы с передаваемыми в них переменными нужно обрабатывать методом prepare():
$sql = $wpdb->prepare( "SELECT * FROM table WHERE id = %s", $var );
esc_sql()
Кроме метода $wpdb->prepare() запрос можно очистить функцией esc_sql(). Но «prepare» предпочтительнее, потому что исправляет некоторые ошибки форматирования.
$name = esc_sql( $name ); $status = esc_sql( $status ); $wpdb->get_var( "SELECT something FROM table WHERE foo = '$name' and status = '$status'" );
ВАЖНО! После esc_sql() очищенную строку можно использовать только внутри кавычек ''
или ""
. Т.е. правильно писать field = '$value'
, а не field = $value
, где $value = esc_sql( $value );
Метод класса: wpdb{}
Хуков нет.
Возвращает
Строку|null
. Sanitized query string, if there is a query to prepare.
Использование
global $wpdb; $wpdb->prepare( $query, ...$args );
- $query(строка) (обязательный)
Строка запроса. В нем можно использовать заменители:
%d
- число%s
- строка%f
- дробное число (число с плавающей точкой, с версии 3.3).
- ...$args(строка/число/массив)
Переменные, которые будут использованы для замены плейсхолдеров
%s %d %f
в строке запроса.Эти переменные можно указать через запятую (как дополнительные параметры функции) или в массиве:
$wpdb->prepare( 'query', $param1, $param2 )
$wpdb->prepare( 'query', [ $param1, $param2 ] )
.
Примеры
#1 Демонстрация работы
$wpdb->prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d OR `other_field` LIKE %s", [ 'foo', 1337, '%bar' ] ); $wpdb->prepare( "SELECT DATE_FORMAT(`field`, '%%c') FROM `table` WHERE `column` = %s", 'foo' );
#2 Добавим произвольное поле к посту 10
Из примера видно, что с prepare() нет необходимости заботиться об экранировании кавычек и прочего, что может навредить запросу.
$metakey = "'крах' БД"; $metavalue = "WordPress может 'сломать' Базу Данных если не экранировать запрос."; $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", 10, $metakey, $metavalue ) );
#3 Передача параметров в виде массива
Это такой же пример, только тут все переменные передаются во втором параметре в виде массива.
Передавать параметры в виде массива, может быть полезно, когда мы заранее не знаем количество аргументов, которые нужно будет передать.
$metakey = "'крах' БД"; $metavalue = "WordPress может 'сломать' Базу Данных если не экранировать запрос."; $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", array( 10, $metakey, $metavalue ) ) );
#4 Очистка значений для WHERE IN условия
Это полезно в случаях, когда у вас есть массив значений, который нужно передать в IN условие запроса. Кол-во элементов массива может быть разное, поэтому заполнители нужно создавать динамически:
$in_values = [ 'one', 'two' ]; $in_pholders = implode( ',', array_fill( 0, count( $in_values ), '%s' ) ); $sql = $wpdb->prepare( "SELECT $wpdb->posts WHERE post_type = %s WHERE post_name IN ( $in_pholders )", [ 'page', ...$in_values ] ); echo $sql; // SELECT wp_posts WHERE post_type = 'page' WHERE post_name IN ( 'one','two' )
Список изменений
С версии 2.3.0 | Введена. |
С версии 5.3.0 | Formalized the existing and already documented ...$args parameter by updating the function signature. The second parameter was changed from $args to ...$args. |