Вопрос или проблема
У меня есть следующая функция PHP, которая запрашивает некоторые пользовательские таблицы базы данных и выводит результат. Обычно это работает отлично, за исключением случаев, когда я вставляю апостроф в запрос; например, “Men’s” или “Women’s” вызывает такие ошибки, как ниже. Как я могу это исправить? Я немного новичок в SQL, поэтому, уверен, я делаю что-то явно неправильно.
[03-Oct-2024 16:12:10 UTC] Ошибка базы данных WordPress У вас ошибка в синтаксисе SQL; проверьте руководство, которое соответствует версии вашего сервера MySQL для правильного синтаксиса вблизи 's%' ИЛИ `description` LIKE '%Men\\'s%' ИЛИ `code` LIKE '%Men\\'s%') ORDER BY `titl' на строке 1 для запроса SELECT * FROM `83kg5a_activity_posts` WHERE ( `title` LIKE '%Men\\'s%' ИЛИ `description` LIKE '%Men\\'s%' ИЛИ `code` LIKE '%Men\\'s%') ORDER BY `title` ASC, `code` ASC выполнено do_action('wp_ajax_activity_list'), WP_Hook->do_action, WP_Hook->apply_filters, ActivityPlugin\ajax_activity_list, activity_include, include('/plugins/activity-plugin/views/activity-list.php'), ActivityPlugin\get_activities
/**
* Получить активности, опционально с запросом
*
* @param array<mixed> $options
* @return array|null
*/
function get_activities(array $options = []): ?array {
global $wpdb;
/**
* Получить настройки
*/
$settings = get_settings();
/**
* Конвертировать запрос
*/
if (array_key_exists("q", $_GET) && $_GET["q"]) {
$options[] = [
[
"key" => "title",
"operator" => "LIKE",
"value" => "%" . $wpdb->esc_like($_GET["q"]) . "%",
],
[
"key" => "description",
"operator" => "LIKE",
"value" => "%" . $wpdb->esc_like($_GET["q"]) . "%",
],
[
"key" => "code",
"operator" => "LIKE",
"value" => "%" . $wpdb->esc_like($_GET["q"]) . "%",
],
];
}
/**
* Конвертировать термины
*/
foreach ($_GET["terms"] as $data) {
$options[] = [
"key" => $data["name"],
"operator" => "=",
"value" => $data["value"],
];
}
/**
* Конвертировать DOW
*/
if (array_key_exists("dow", $_GET) && count($_GET["dow"]) < 7) {
$options[] = array_map(function($day) use ($wpdb) {
return [
"key" => "schedule",
"operator" => "LIKE",
"value" => "%" . $wpdb->esc_like($day) . "%",
];
}, $_GET["dow"]);
}
/**
* Подготовить запрос
*/
$query = "SELECT * FROM `{$wpdb->prefix}activity_posts`";
/**
* Добавить параметры к запросу
*/
if ($options) {
$query .= " WHERE";
/**
* Добавить параметры
*/
foreach ($options as $data) {
if (array_key_exists(0, $data)) {
$query .= " (";
foreach ($data as $value) {
$query .= " `{$value["key"]}` {$value["operator"]} '{$value["value"]}' OR";
}
$query = rtrim($query, " OR");
$query .= ") AND";
} else {
$query .= " `{$data["key"]}` {$data["operator"]} '{$data["value"]}' AND";
}
}
$query = rtrim($query, " AND");
}
/**
* Конвертировать возраст
*/
$queried_min = (array_key_exists("age_minimum", $_GET) ? intval($_GET["age_minimum"]) : $settings["age_limit_min"]) * 12;
$queried_max = (array_key_exists("age_maximum", $_GET) ? intval($_GET["age_maximum"]) : $settings["age_limit_senior"]) * 12;
$min_query = "";
$max_query = "";
if ($queried_min !== $settings["age_limit_min"] * 12) {
$min_query = " `age_minimum` >= {$queried_min}";
}
if ($queried_min !== $settings["age_limit_min"] * 12 || $queried_max !== $settings["age_limit_senior"] * 12) {
if ($queried_max >= $settings["age_limit_senior"] * 12) {
$max_query .= " `age_maximum` >= {$queried_max}";
} elseif ($queried_max >= $settings["age_limit_adult"] * 12) {
$max_query .= " `age_maximum` >= {$queried_max} AND `age_maximum` <= " . ($settings["age_limit_senior"] * 12);
} elseif ($queried_max > $settings["age_limit_min"] * 12) {
$max_query .= " `age_maximum` <= {$queried_max}";
}
}
if ($min_query || $max_query) {
$query .= $options ? " AND" : " WHERE";
if ($min_query && $max_query) {
$query .= " ({$min_query} AND {$max_query})";
} elseif ($min_query) {
$query .= $min_query;
} elseif ($max_query) {
$query .= $max_query;
}
}
/**
* Установить порядок
*/
$query .= " ORDER BY `title` ASC, `code` ASC";
/**
* Логировать запрос
*/
if (WP_DEBUG_LOG) {
error_log($query);
}
/**
* Получить все кэшированные активности из базы данных
*/
return $wpdb->get_results($query);
}
Рекомендуется использовать метод wpdb::prepare()
для правильного создания запросов и предотвращения SQL-инъекций.
Для базового решения используйте функцию addslashes()
, но этого недостаточно для предотвращения SQL-инъекций.
Ответ или решение
Чтобы устранить ошибки SQL, связанные с использованием апострофов в запросах, вы можете воспользоваться несколькими подходами. Ваша текущая реализация, похоже, не должным образом обрабатывает специальные символы, такие как апострофы, из-за чего возникает ошибка синтаксиса. Давайте рассмотрим, как можно это исправить.
1. Использование wpdb::prepare()
Рекомендованным методом для формирования запросов в WordPress является использование функции wpdb::prepare()
. Этот метод не только эффективно обрабатывает апострофы и другие специальные символы, но и защищает от SQL-инъекций.
Вот примеры, как можно изменить ваш код:
if (array_key_exists("q", $_GET) && $_GET["q"]) {
$search_query = $wpdb->esc_like($_GET["q"]);
$options[] = [
[
"key" => "title",
"operator" => "LIKE",
"value" => "%$search_query%",
],
[
"key" => "description",
"operator" => "LIKE",
"value" => "%$search_query%",
],
[
"key" => "code",
"operator" => "LIKE",
"value" => "%$search_query%",
],
];
}
2. Формирования динамического запроса с wpdb::prepare()
В конце формирования вашего запроса вы можете использовать метод wpdb::prepare()
чтобы избежать ошибок с синтаксисом и повысить безопасность:
if ($options) {
$query .= " WHERE ";
foreach ($options as $data) {
if (array_key_exists(0, $data)) {
$query .= " (";
foreach ($data as $value) {
// Использование prepare для обработки специальных символов
$query .= " `{$value["key"]}` {$value["operator"]} % OR";
}
$query = rtrim($query, " OR");
$query .= ") AND";
} else {
$query .= " `{$data["key"]}` {$data["operator"]} % AND";
}
}
$query = rtrim($query, " AND");
}
// Подготовка запроса с использованием prepare
$prepared_query = $wpdb->prepare($query, ...array_map(function($data) {
return $data["value"];
}, array_merge(...$options)));
// В итоге, выполняем запрос
return $wpdb->get_results($prepared_query);
3. Альтернативный метод: использование addslashes()
Если по каким-то причинам вы не можете использовать wpdb::prepare()
, простой метод — это использовать функцию addslashes()
, хотя это менее безопасный подход:
$value = addslashes($_GET["q"]);
$query .= " `{$value["key"]}` {$value["operator"]} '$value' AND";
Имейте в виду, что addslashes()
происходит лишь экранирование специальных символов, и вы не защищены от SQL-инъекций, если не используете подготовленные выражения.
Заключение
Современные подходы к безопасности баз данных в PHP не оставляют места для сомнений в необходимости использования методов, подобных wpdb::prepare()
. Применяя рекомендованные практики, вы не только устраняете ошибки типографии и специальный символов, но и обеспечиваете безопасность своего приложения. Следуя этим рекомендациям, вы можете избежать ошибок SQL и делать свои приложения более защищенными.