MySQL задыхается от фигурных (умных) кавычек

Я вставляю некоторые данные в базу данных из формы. Я использую addslashes для экранирования текста (также пробовал mysql_real_escape_string с тем же результатом).

Обычные кавычки экранированы, но некоторые другие кавычки - нет. Например, строка:

Кровь Гомера становится секретным ингредиентом нового пива Мо.

преобразуется в:

Кровь Гомера становится секретным ингредиентом нового пива Мо.

Я не думал, что фигурная цитата будет иметь значение без экранирования, но в базу данных вставляется только этот текст:

Кровь Гомера становится секретным ингредиентом в Мо.

Таким образом, PHP считает, что фигурная кавычка в порядке, но MySQL теряет строку. Однако MySQL не выдает никаких ошибок.


person DisgruntledGoat    schedule 23.08.2009    source источник
comment
Хороший вопрос, интересно, происходит ли это с подготовленными заявлениями.   -  person Allain Lalonde    schedule 23.08.2009
comment
@Allain: Я бы так не подумал, но если кто-то хочет проверить, вперед. Я должен пояснить, что знаю о подготовленных операторах, это какой-то старый код, который до недавнего времени работал на PHP 4.   -  person DisgruntledGoat    schedule 23.08.2009


Ответы (2)


Я бы искал несоответствие между кодировкой символов, используемой в вашем веб-интерфейсе, и кодировкой, используемой на уровне базы данных. Если ваш веб-интерфейс использует, например, кодировку UTF-8, а ваша база данных использует кодировку MySQL по умолчанию latin1, вам необходимо настроить свои таблицы с кодировкой DEFAULT CHARSET=utf8.

Кстати, используйте mysql_real_escape_string() или mysqli. addslashes() НЕ является адекватной защитой от SQL-инъекций.

person chaos    schedule 23.08.2009
comment
Добавление +1 никогда не должно использоваться ни для чего. Это действительно будет проблема с набором символов; мое предположение, учитывая, что символы кавычек фактически исчезают, заключается в том, что они представляют собой байты ISO-8859-1, вставляемые в базу данных UTF-8. На самом деле вы хотите, чтобы все было в UTF-8; начните с обслуживания ваших страниц с этой кодировкой, и это гарантирует, что отправленные формы также будут поступать в UTF-8. - person bobince; 23.08.2009
comment
Да, это было потому, что веб-страница была не в UTF8, а в MySQL. Дополнительный вопрос: есть ли функция, противоположная mysql_real_escape_string? В мануале ничего не нашел. - person DisgruntledGoat; 24.08.2009
comment
Неа. Наверное, потому что сложно представить, зачем он вам нужен. Если вы извлекаете данные из MySQL, надеюсь, очевидно, что вам не нужно отменять экранирование. Если по какой-то причине вам нужны исходные данные, прежде чем вы загрузите их в MySQL, просто не избавляйтесь от исходных данных при создании экранированной версии. - person chaos; 24.08.2009
comment
Это было потому, что у меня была функция, которая рекурсивно добавляла/удаляла косые черты (см. " title="php не работает по ссылке в рекурсивной функции"> stackoverflow.com/questions/1216552/). Идея заключалась в том, чтобы привести все данные формы в согласованное состояние после отправки, например. удалите магические кавычки, при необходимости обработайте данные, а затем добавьте их обратно для запроса MySQL. Я думаю, что скоро перейду на параметризованные запросы, это избавит от головной боли!! - person DisgruntledGoat; 24.08.2009
comment
Ах, хорошо. IMO единственная стоящая вещь, которую можно сделать с магическими кавычками, — это удалить их, поэтому для этого вам просто нужно stripslashes(). :) - person chaos; 24.08.2009

’ in Moe’s — единственный символ в строке вашего примера, который не будет допустимым, если эта строка закодирована в кодировке latin1, но ваш сервер mysql ожидает utf8.

Простая демонстрация:

<?php
function foo($s) {
    echo 'len=', strlen($s), ' ';
  for($i=0; $i<strlen($s); $i++) {
    printf('%02X ', ord($s[$i]));
  }
  echo "\n";
}

 // my file is latin1 encoded and so is the string literal
foo('Moe’s');
// now try it with an utf8 encoded string
foo( utf8_encode('Moe’s') );

печатает

len=5 4D 6F 65 92 73
len=6 4D 6F 65 C2 92 73

Поэтому возникает вопрос: вы передаете серверу mysql что-то в "неправильной" кодировке?
Каждое соединение имеет кодировку соединения, и сервер mysql ожидает, что ваш клиент (скрипт php) отправит данные, закодированные в этом наборе символов. Вы можете узнать, с какой кодировкой соединения

SHOW VARIABLES LIKE '%character%'

как в

$mysql = mysql_connect('..', '..', '..') or die(mysql_error());
mysql_select_db('..', $mysql) or die(mysql_error());

$query = "SHOW VARIABLES like '%character%'";
$result = mysql_query($query, $mysql) or die(__LINE__.mysql_error());
while( false!==($row=mysql_fetch_array($result, MYSQL_ASSOC)) ) {
  echo join(', ', $row), "\n";
}

Это должно напечатать что-то вроде

character_set_client, utf8
character_set_connection, utf8
character_set_database, latin1
character_set_filesystem, binary
character_set_results, utf8
character_set_server, utf8
character_set_system, utf8

а character_set_connection, utf8 указывает, что "мой" набор символов подключения - utf8, т.е. сервер mysql ожидает символы в кодировке utf8 от клиента (php). Какая "ваша" кодировка соединения?

Затем взгляните на фактическую кодировку вашей строки параметров, т.е. если у вас было

$foo = mysql_real_escape_string($_POST['foo'], $mysql);

заменить это на

echo '<div>Debug hex($_POST[foo])=';
for($i=0; $i<strlen($s); $i++) {
    printf('%02X ', ord($_POST['foo'][$i]));
}
echo "</div>\n";
$foo = mysql_real_escape_string($_POST['foo'], $mysql);

и проверьте фактическую кодировку вашей входной строки. Он печатает 92 или C2 92?

person VolkerK    schedule 23.08.2009