Похоже, что использовать mysql_set_charset
/ mysqli::set_charset
вместо прямых set names
запросов MySQL.
Часто упоминаемая причина заключается в том, что set names
небезопасны, потому что кодировка, используемая для mysql_real_escape_string
/ mysqli::real_escape_string
будет установлена только вызовом mysql_set_charset
/ mysqli::set_charset
. (Другая причина, о которой идет речь, заключается в том, что в документах PHP говорится, что это «не рекомендуется».)
Однако можно ли использовать прямые set names
запросов MySQL MySQL, если мы будем использовать подготовленные инструкции и другие способы экранирования, кроме mysql_real_escape_string
/ mysqli::real_escape_string
/ mysqli_escape_string
?
Помимо влияния на кодировку mysql_real_escape_string
/ mysqli::real_escape_string
/ mysqli_escape_string
, существует ли разница между set names
mysql_set_charset
vs mysql_set_charset
/ mysqli::set_charset
?
Вызов SET NAMES
в соединении эквивалентен вызову set_charset
, если вы не вызываете ни get_charset
ни mysql_real_escape_string
(и друзей).
Когда вы вызываете set_charset
, PHP делает две вещи. Во-первых, он вызывает SET NAMES
в соединении. Во-вторых, он помнит, какую кодировку вы задали. Эта информация о состоянии позже используется только в get_charset
и mysql_real_escape_string
(и друзей). Поэтому, если вы не используете эти функции, вы можете рассмотреть два эквивалента.
Пойдем по источнику:
mysql_set_charset
и mysqli_set_charset
call … mysql_set_character_set
функции mysql_set_character_set
… Engine macro mysqlnd_set_character_set
, который определяется как:
#define mysqlnd_set_character_set(conn, cs) \ ((conn)->data)->m->set_charset((conn)->data, (cs)))
и расширяется до …
MYSQLND_METHOD(mysqlnd_conn_data, set_charset)
который содержит следующий код (пронумерованный для обсуждения, это не фактические номера строк исходного кода): 1 if (PASS == conn->m->local_tx_start(conn, this_func)) { 2 char * query; 3 size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname); 4 5 if (FAIL == (ret = conn->m->query(conn, query, query_len))) { 6 php_error_docref(NULL, E_WARNING, "Error executing query"); 7 } else if (conn->error_info->error_no) { 8 ret = FAIL; 9 } else { 10 conn->charset = charset; 11 } 12 mnd_sprintf_free(query); 13 14 conn->m->local_tx_end(conn, this_func, ret); 15 }
Как вы можете видеть, PHP вызывает SET NAMES
на самом соединении (строка 3). PHP также отслеживает только набор символов (строка 10). Комментарии далее обсуждают, что происходит с conn->charset
, но достаточно сказать, что это заканчивается только в get_charset
и mysql_real_escape_string
(и друзьях).
Итак, если вы не заботитесь об этом состоянии, и вы соглашаетесь использовать ни get_charset
ни mysql_real_escape_string
, вы можете называть SET NAMES
на самом соединении без какого-либо плохого эффекта.
В стороне, и я никогда не делал этого, но похоже, что компиляция PHP с -DPHP_DEBUG=1
позволит обеспечить значительную отладку через различные макросы DBG
. Это может быть полезно, когда вы видите, как ваш код проходит через этот блок.
Необходимо сделать две вещи (в этой области):
INSERTs
/ SELECTs
будут знать, как изменить байты во время записи / чтения. Сначала нужно избегать апострофа и двойной кавычки, так как оба из них являются допустимыми метками кавычек для строк в синтаксисе MySQL. Тогда сам побеждающий символ нуждается в побеге. Эти 3 символа достаточны для обязательных приложений. Однако, если вы пытаетесь избежать BLOB
(например, .jpg), различные управляющие символы могут вызвать проблемы. Вероятно, вам лучше конвертировать в hex, а затем использовать UNHEX()
, чтобы избежать проблем. Примечание. Здесь ничего не сказано о наборах символов. Если вы не имеете дело с BLOBs
, вы можете уйти с addslashes()
.
Вторая цель заключается в том, чтобы сказать: «Этот поток байтов кодируется таким образом (utf8 / latin1 / etc)». Используется только для преобразования между CHARACTER SET
столбца, который хранится / извлекается, и желаемой кодировкой в вашем клиенте (PHP и т. Д.). На разных языках он обрабатывается различными способами. Для PHP:
mysql_*
– Не используйте этот интерфейс; он устарел и вскоре будет удален. mysqli_*
– mysqli::set_charset(...)
new PDO('...;charset=UTF8', ...)
Делает ли set_charset()
что-то с real_escape_string? Я не знаю. Но это не должно иметь значения. SET NAMES
явно не может, поскольку это команда MySQL, и ничего не знает о PHP.
htmlentities()
– это еще одна функция PHP в этой области. Он превращает 8-битные коды в &
сущности. Это не следует использовать в MySQL. Это замаскировало бы другие проблемы. Используйте его только в определенных ситуациях, связанных с HTML, а не с PHP или MySQL.
Единственными разумными CHARACTER SETs
будут использоваться сегодня, являются ascii, latin1, utf8 и utf8mb4. У них нет «символов» в области «управления». Sjis и несколько других наборов символов. Эта путаница над управляющими символами может быть причиной существования real_escape_string.
Вывод:
Как я вижу, вам нужны два механизма: один для экранирования и один для установки кодировки в клиенте. Они отделены друг от друга.
Если они связаны друг с другом, руководство по PHP не предоставило каких-либо веских оснований для выбора одного метода над другим.
mysql: весь интерфейс устарел, поэтому не используйте его вообще (PHP 7 удаляет интерфейс).
mysqli (и PDO) подготовил операторы, которые не используют (и не хотят) использование real_escape_string
. -> Итак, если вы используете только mysqli и только подготовленные инструкции: не стоит беспокоиться о том, как вы устанавливаете кодировку.
Поскольку вы заботитесь о безопасности: я не вижу смысла в том, чтобы не использовать подготовленные заявления.
После того, как вы используете подготовленные операторы mysqli, единственный путь вперед – использовать $mysqli->set_charset()
поскольку вы не можете просто конкатенировать несколько операторов sql в одной строке.
Следовательно, вопрос знать разницу в большинстве академических и не имеет значения в реальной жизни.
В итоге:
mysql: не использовать вообще.
mysqli: использовать подготовленные операторы и, следовательно, метод set_charset()
Также: вам больше не понадобится real_escape_string после использования подготовленных операторов.
или, конечно, использовать PDO и его методы.
SET NAMES ...
– псевдоним удобства:
SET NAMES 'charset_name'
эквивалентен этим трех операторам:SET character_set_client = charset_name; SET character_set_results = charset_name; SET character_set_connection = charset_name;
Установка character_set_connection в charset_name также неявно устанавливает collation_connection для сопоставления по умолчанию для
charset_name
.
… который предоставляет MySQL Server всю информацию о кодировании текста, необходимую для текущего соединения. Все идет нормально.
Но PHP также задействован, и он ничего не узнает отсюда, потому что это в основном случайный пользовательский запрос. Есть две вещи, которые PHP не будет делать по очевидным причинам производительности:
Короче: этот метод уведомляет сервер, но не клиент. Однако выделенные функции PHP выполняют обе вещи.