TLDR
// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');
В этом ответе акцент делается на pdo-библиотеке php, потому что она такая вездесущая.
Краткое напоминание - mysql - это клиент-серверная архитектура. Это важно, потому что есть не только сервер mysql, где находится настоящая база данных, но также есть отдельный клиентский клиент mysql, который общается с сервером mysql (это отдельные объекты). Можно сказать, что клиент mysql и pdo смешаны.
Когда вы используете set names utf8
, вы отправляете стандартный запрос sql для mysql. В то время как запрос sql проходит через pdo, а затем через клиентскую библиотеку mysql и, наконец, достигает сервера mysql, ТОЛЬКО сервер mysql анализирует и интерпретирует этот запрос sql. Это важно, потому что сервер mysql не отправляет никаких сообщений обратно pdo или клиенту mysql, сообщая ему, что набор символов и кодировка изменились, и поэтому клиент mysql и pdo абсолютно не знают, что это произошло.
Важно не делать этого, потому что клиентская библиотека не может правильно обрабатывать строки, если она не знает о текущем наборе символов. Наиболее распространенные операции будут работать правильно, если клиент не будет знать правильный набор символов, но не будет экранирования строки, например PDO :: quote . Вы можете подумать, что вам не нужно беспокоиться о ручном экранировании примитивной строки, потому что вы используете подготовленные операторы, но на самом деле подавляющее большинство пользователей pdo: mysql неосознанно используют эмулированные подготовленные операторы , потому что это было по умолчанию установка для драйвера pdo: mysql уже очень давно. Эмулируемый подготовленный оператор не использует настоящие нативные подготовленные операторы mysql, как это предусмотрено mysql api; вместо этого php эквивалентен вызову PDO::quote()
для всех ваших значений и str_replacing'у для всех ваших заполнителей с указанными для вас значениями.
Поскольку вы не можете правильно экранировать строку, если не знаете используемый набор символов, эти эмулированные подготовленные операторы уязвимы для внедрения SQL, если вы переключились на определенные наборы символов с помощью set names
. Независимо от возможности внедрения SQL, вы все равно можете разорвать ваши строки, если используете схему перехода, предназначенную для другого набора символов.
Для драйвера pdo mysql вы можете указать набор символов при подключении, указав в DSN . Если вы это сделаете, клиентская библиотека и сервер будут знать о наборе символов, и поэтому все будет работать так, как должно.
// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');
Но неправильное экранирование строки - не единственная проблема. Например, у вас также могут возникнуть проблемы с использованием PDO :: bindColumn , поскольку имена столбцов указываются в виде строк, и поэтому снова имеет значение кодировка. Примером может быть имя столбца с именем ütube
(обратите внимание на умляут), и вы переключаетесь с latin
на utf8
с помощью набора имен, а затем вы пытаетесь $stmt->bindColumn('ütube', $var);
с ütube
, являющимся строкой в кодировке utf8, потому что Ваш php файл имеет кодировку utf8. Это не сработает, вам нужно будет закодировать строку как вариант latin1 ... и теперь у вас происходит все виды сумасшествия.