Это кажется общеизвестным использовать 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 написано, что это «не рекомендуется» §.)
Однако безопасно ли использовать прямой запрос 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
против 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
вызов…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
макросы. Это может быть полезно, чтобы увидеть, как ваш код проходит через этот блок.
Необходимо сделать две вещи (в этой области):
INSERTs
/SELECTs
будет знать, как изменить байты во время записи / чтения.Первый должен избегать апострофа и двойных кавычек, поскольку оба они являются приемлемыми кавычками для строк в синтаксисе MySQL. Затем самому убегающему персонажу нужно бежать. Эти 3 символа достаточно для обязательных приложений. Однако, если вы пытаетесь избежать BLOB
(например, .jpg), различные управляющие символы могут вызвать проблемы. Вам, вероятно, лучше конвертировать в гекс, чем использовать UNHEX()
, чтобы избежать проблем. Примечание: здесь ничего не упоминается о наборах символов. Если вы не имеете дело с BLOBs
, вы можете сойти с PHP 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 делают обе вещи.