PHP: критика перехода с оригинального API MySQL на mysqli и PDO
Оригинальный API MySQL (функции mysql_*() — например, mysql_query() и пр.) с версии PHP 5.5.0 объявлен устаревшим. Вместо него разработчики PHP рекомендуют использовать модуль mysqli или объекты данных PHP (PDO). Эти средства обладают расширенным по сравнению с традиционным API функционалом, но действительно ли они удобнее в повседневной практике?
mysqli
mysqli — "MySQL improved extension" ("улучшенный модуль MySQL") — прямой наследник оригинального API MySQL, обладающий более широкими возможностями. Чтобы почувствовать разницу, достаточно посмотреть на список его методов и сравнить с таковым оригинального модуля. Однако нужно отметить, что отличие в реальных возможностях на самом деле только одно: mysqli имеет возможность отправки множественных запросов1. Хотя иметь такую возможность и удобно, на практике её наличие ощутимой роли не играет2.
Другими словами, разницы в реальных возможностях обычного и улучшенного модулей практически нет. Зато есть разница в интерфейсе. Рассмотрим, как в простейшем случае выглядит самая обычная операция — установление соединения с сервером MySQL и выполнение запроса:
mysql_connect('host', 'user', 'password');
mysql_query("SELECT 1");
// улучшенный модуль, процедурный интерфейс
$mysqli = mysqli_connect('host', 'user', 'password');
mysqli_query($mysqli, "SELECT 1");
// улучшенный модуль, объектный интерфейс
$mysqli = new mysqli($host, $user, $password);
$mysqli->query("SELECT 1");
Здесь важно заметить, что функция mysql_query() не требует передавать в явном виде указатель на соединение (хотя и позволяет это делать). В подавляющем большинстве случаев для работы требуется только одно соединение с MySQL, и, однажды установив его, вполне удобно и естественно просто вызывать mysql-функции, не ссылаясь на соединение в явном виде каждый раз.
Функция же mysqli_query(), напротив, требует передавать объект, указывающий на соединение. Этот объект потребуется во всех местах, где нужно будет делать запрос. Это означает, что в коде с локальной областью видимости — функциях и классах — придется либо заводить глобальную переменную, либо передавать этот объект явно.
Это, казалось бы, небольшое отличие на практике не только приводит к необходимости писать более длинный код, но и осложняет миграцию с оригинального модуля на улучшенный из-за необходимости вносить в код изменения.
Резюмируя сказанное, приходится констатировать, что на практике модуль mysqli не обладает какой-то дополнительной полезностью по сравнению с оригинальным. Пользоваться же им менее удобно.
PDO
PHP Data Object (Объекты данных PHP) — расширение языка, определяющее абстрактный интерфейс доступа к базам данных (это означает, что одни и те же методы PDO могут использоваться для разных СУБД).
На практике реально ощутимым отличием PDO от других интерфейсов к MySQL является возможность легко делать следующие две операции:
- вставку в запрос параметров с экранированием
- получение результата запроса в виде ассоциативного массива
Работа с MySQL, в основном, и заключается именно в этих двух вещах, поэтому многие разработчики останавливают свой выбор на PDO.
Однако есть один нюанс. Рассмотрим, как в самом простом случае средствами PDO выполняется запрос:
$query = $DBH->prepare("SELECT 1"); // готовим запрос
$query->execute(); // выполняем запрос
Важно отметить, что для отправки запроса требуется вызывать не один метод, а два — prepare(), а затем execute(). На уровне механизма СУБД в данном случае задействуются т.н. prepared statements — специальный инструмент СУБД, позволяющий ускорить последовательное выполнение повторяющихся запросов, построенных по одному и тому же шаблону.
Вместе с тем, на практике идущие подряд однотипные запросы встречаются довольно редко. Напротив, обычно запросы разные, и это полностью нивелирует положительный эффект от предварительного разбора. Более того, в конечном итоге вместо одного запроса к MySQL делается два, в результате чего схема с prepare() и execute() оказывается даже медленнее обычного одиночного запроса.PDO позволяет выполнить запрос и напрямую — для этого предназначен метод query(). Однако при использовании этого метода вставку в запрос параметров и их экранирование приходится проводить вручную, поэтому query() не пользуется популярностью и разработчики предпочитают связку из prepare() и execute() в любом случае, потому что это удобнее.
Следует также подчеркнуть, что при использовании PDO (как и в случае с mysqli) есть необходимость заводить объект, который в областях видимости функций и классов недоступен.
Резюмируя сказанное, нужно отметить, что по сравнению с другими модулями PDO предоставляет несколько более удобный функционал, однако реализация его выполнена неоптимально. Кроме того, высокий уровень абстракции интерфейса плохо сказывается на осведомлённости разработчиков о происходящем на стороне MySQL-сервера.
Что делать?
Одним из решений всех вышеперечисленных проблем является библиотека удобных функций MySQL, при разработке которой основной целью было сделать программирование самых распространенных операций при работе с MySQL (таких как сохранение результата запроса в виде ассоциативного массива, обработка ошибок, подстановка массива в запрос) максимально удобным. Вот несколько примеров:
// библиотека будет пользоваться инструментами mysqli,
// в противном случае - оригинального модуля MySQL
// включаем mysqli
$mysqli = mysqli_connect(...);
// простая отправка запроса
mysql_q("TRUNCATE sometable");
// получение результата скалярного (один столбец, одна строка) запроса
$now = mysql_getcell("SELECT NOW()");
// получение строки таблицы:
$row = mysql_getrow("
SELECT *
FROM watches
WHERE id = 1052
");
// получение столбца в виде одномерного массива:
$ids = mysql_getcolumn("
SELECT id
FROM watches
WHERE mark = 'Edox' AND price > 5000
");
// получение записей таблицы с подстановкой в запрос параметров:
$sql = "
SELECT *
FROM watches
WHERE mark = :mark AND price > :price
";
$params = array( 'mark' => 'Edox', 'price' => 5000 );
$data = mysql_gettable($sql, FALSE, $params);
// подстановку можно делать во всех вышеперечисленных функциях
Подробнее о библиотеке можно прочитать в соответствующей статье.
1. ▲ Работу с транзакциями, хранимыми процедурами и прочим, заявленным в документации как отличия между модулями, на самом деле можно реализовывать с помощью собственно SQL-запросов: например, вместо $mysqli->commit() писать mysql_query("COMMIT"), и т.п.
2. ▲ Разница в производительности множественного запроса и соответствующего количества одиночных сводится к затратам на отправку запроса из PHP, которые по сравнению с затратами на собственно их выполнение механизмом СУБД как правило ничтожно малы. Кроме того, ситуация, когда запросы следуют подряд без необходимости промежуточной обработки результатов, является довольно редкой.
© Все права на данную статью принадлежат порталу webew.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.