"установить имена" и mysqli_set_charset - кроме того, что они влияют на mysqli_escape_string, они идентичны?
Это кажется общеизвестным для использования mysql_set_charset
/mysqli::set_charset
вместо прямого запроса MySQL set names
.
Часто упоминаемая причина заключается в том, что set names
небезопасен, потому что кодировка, используемая для mysql_real_escape_string
/mysqli::real_escape_string
будет задан только вызовом mysql_set_charset
/mysqli::set_charset
. (Другая причина, о которой идет речь, заключается в том, что в документах PHP говорится, что "не рекомендуется" & sect;.)
Однако безопасно ли использовать прямой запрос MySQL set names
, если мы будем использовать подготовленные заявления и другие способы ускорения, кроме mysql_real_escape_string
/mysqli::real_escape_string
/mysqli_escape_string
?
Помимо влияния на кодировку mysql_real_escape_string
/mysqli::real_escape_string
/mysqli_escape_string
, существует ли разница между set names
vs mysql_set_charset
/mysqli::set_charset
?
Ответы
Ответ 1
Вызов SET NAMES
в соединении эквивалентен вызову set_charset
, если вы не вызываете ни get_charset
, ни mysql_real_escape_string
(и друзей).
Когда вы вызываете set_charset
, PHP выполняет две вещи. Во-первых, он вызывает SET NAMES
в соединении. Во-вторых, он помнит, какую кодировку вы задали. Эта информация о состоянии позже используется только в функциях get_charset
и mysql_real_escape_string
(и друзей). Поэтому, если вы не используете эти функции, вы можете рассмотреть два эквивалента.
Пропустите исходный код:
- Функции Userland
mysql_set_charset
и mysqli_set_charset
call...
- Функция двигателя
mysql_set_character_set
вызывает...
-
Макрос двигателя 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
. Это может быть полезно при просмотре того, как ваш код проходит через этот блок.
Ответ 2
Необходимо сделать две вещи (в этой области):
- Сбросить кавычки (и другие символы) перед тем, как поместить их в кавычки. В противном случае кавычки дадут вам синтаксические ошибки.
- Установить кодировку байтов в клиенте. Это значит, что
INSERTs
/SELECTs
будет знать, как изменить байты во время записи/чтения.
Сначала нужно избегать апострофа и двойной кавычки, так как оба из них являются допустимыми метками кавычек для строк в синтаксисе MySQL. Тогда сам побеждающий символ нуждается в побеге. Эти 3 символа достаточны для обязательных приложений. Однако, если вы пытаетесь избежать BLOB
(например,.jpg), различные управляющие символы могут вызвать проблемы. Вероятно, вам лучше конвертировать в hex, а затем использовать UNHEX()
, чтобы избежать проблем. Примечание. Здесь ничего не сказано о наборах символов. Если вы не имеете дело с BLOBs
, вы можете уйти с PHP addslashes()
.
Вторая цель заключается в том, чтобы сказать: "Этот поток байтов кодируется таким образом (utf8/latin1/etc)". Он используется только для преобразования между CHARACTER SET
столбца, который хранится/извлекается, и требуемой кодировкой в вашем клиенте (PHP и т.д.). На разных языках он обрабатывается различными способами. Для PHP:
-
mysql_*
- Не используйте этот интерфейс; он устарел и скоро будет удален.
-
mysqli_*
- mysqli::set_charset(...)
- PDO -
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 не предоставило каких-либо веских оснований для выбора одного метода над другим.
Ответ 3
mysql: весь интерфейс устарел, поэтому не используйте его вообще (PHP 7 удаляет интерфейс).
mysqli (и PDO) подготовил операторы, которые используют real_escape_string
не нужно (и не хотят).
- > Итак, если вы используете только mysqli и только подготовленные операторы: не беспокойтесь, как вы устанавливаете кодировку.
Поскольку вы заботитесь о безопасности: я не вижу смысла в том, чтобы не использовать подготовленные инструкции.
Как только вы используете подготовленные операторы mysqli, единственный путь вперед - использовать $mysqli->set_charset()
, поскольку вы не можете просто конкатенировать несколько операторов sql в одной строке больше.
Следовательно, вопрос знать разницу в большинстве академических и не имеет значения в реальной жизни.
Вкратце:
-
mysql: не использовать вообще.
-
mysqli: используйте подготовленные инструкции и, следовательно, метод set_charset()
Также: вам больше не понадобится real_escape_string после использования подготовленных операторов.
-
или - конечно - используйте PDO и его методы.
Ответ 4
SET NAMES ...
- псевдоним удобства:
Оператор A 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 не будет делать по очевидным причинам производительности:
- Сканировать все пользовательские запросы, отправленные на сервер, для обнаружения вызовов SET NAMES.
- Спросите MySQL о текущих значениях задействованных директив каждый раз, когда что-то нужно сделать.
Вкратце: этот метод уведомляет сервер, но не клиент. Однако выделенные функции PHP выполняют обе вещи.