Optionally, the --with-mysql-sock[=DIR] sets to location to the MySQL unix socket pointer for all MySQL extensions, including PDO_MYSQL. If unspecified, the default locations are searched.
Optionally, the --with-zlib-dir[=DIR] is used to set the path to the libz install prefix.
$ ./configure --with-pdo-mysql --with-mysql-sock=/var/mysql/mysql.sock
Version | Description |
---|---|
5.4.0 | mysqlnd became the default MySQL library when compiling PDO_MYSQL. Previously, libmysqlclient was the default MySQL library. |
5.4.0 | MySQL client libraries 4.1 and below are no longer supported. |
5.3.9 | Added SSL support with mysqlnd and OpenSSL. |
5.3.7 | Added SSL support with libmysqlclient and OpenSSL. |
The constants below are defined by this driver, and will only be available when the extension has been either compiled into PHP or dynamically loaded at runtime. In addition, these driver-specific constants should only be used if you are using this driver. Using driver-specific attributes with another driver may result in unexpected behaviour. PDO::getAttribute() may be used to obtain the PDO::ATTR_DRIVER_NAME attribute to check the driver, if your code can run against multiple drivers.
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (integer ) If this attribute is set to TRUE on a PDOStatement , the MySQL driver will use the buffered versions of the MySQL API. If you"re writing portable code, you should use PDOStatement::fetchAll() instead.Example #1 Forcing queries to be buffered in mysql
if ($db
->
getAttribute
(PDO
::
ATTR_DRIVER_NAME
) ==
"mysql"
) {
$stmt
=
$db
->
prepare
("select * from foo"
,
array(PDO
::
MYSQL_ATTR_USE_BUFFERED_QUERY
=>
true
));
} else {
die("my application only works with mysql; I should use \$stmt->fetchAll() instead"
);
}
?>
Enable LOAD LOCAL INFILE .
PDO::MYSQL_ATTR_INIT_COMMAND (integer )Command to execute when connecting to the MySQL server. Will automatically be re-executed when reconnecting.
Note, this constant can only be used in the driver_options array when constructing a new database handle.
PDO::MYSQL_ATTR_READ_DEFAULT_FILE (integer )Read options from the named option file instead of from my.cnf . This option is not available if mysqlnd is used, because mysqlnd does not read the mysql configuration files.
PDO::MYSQL_ATTR_READ_DEFAULT_GROUP (integer )Read options from the named group from my.cnf or the file specified with MYSQL_READ_DEFAULT_FILE . This option is not available if mysqlnd is used, because mysqlnd does not read the mysql configuration files.
PDO::MYSQL_ATTR_MAX_BUFFER_SIZE (integer )Maximum buffer size. Defaults to 1 MiB. This constant is not supported when compiled against mysqlnd.
PDO::MYSQL_ATTR_DIRECT_QUERY (integer )Perform direct queries, don"t use prepared statements.
PDO::MYSQL_ATTR_FOUND_ROWS (integer )Return the number of found (matched) rows, not the number of changed rows.
PDO::MYSQL_ATTR_IGNORE_SPACE (integer )Permit spaces after function names. Makes all functions names reserved words.
PDO::MYSQL_ATTR_COMPRESS (integer )Enable network communication compression. This is also supported when compiled against mysqlnd as of PHP 5.3.11.
PDO::MYSQL_ATTR_SSL_CA (integer )The file path to the SSL certificate authority.
This exists as of PHP 5.3.7.
PDO::MYSQL_ATTR_SSL_CAPATH (integer )The file path to the directory that contains the trusted SSL CA certificates, which are stored in PEM format.
This exists as of PHP 5.3.7.
PDO::MYSQL_ATTR_SSL_CERT (integer )The file path to the SSL certificate.
This exists as of PHP 5.3.7.
PDO::MYSQL_ATTR_SSL_CIPHER (integer )A list of one or more permissible ciphers to use for SSL encryption, in a format understood by OpenSSL. For example: DHE-RSA-AES256-SHA:AES128-SHA
This exists as of PHP 5.3.7.
PDO::MYSQL_ATTR_SSL_KEY (integer )The file path to the SSL key.
This exists as of PHP 5.3.7.
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT (integer )Provides a way to disable verification of the server SSL certificate.
This exists as of PHP 7.0.18 and PHP 7.1.4.
PDO::MYSQL_ATTR_MULTI_STATEMENTS (integer )Disables multi query execution in both PDO::prepare() and PDO::query() when set to FALSE .
Note, this constant can only be used in the driver_options array when constructing a new database handle.
This exists as of PHP 5.5.21 and PHP 5.6.5.
The behaviour of these functions is affected by settings in php.ini .
Name | Default | Changeable |
---|---|---|
pdo_mysql.default_socket | "/tmp/mysql.sock" | PHP_INI_SYSTEM |
pdo_mysql.debug | NULL | PHP_INI_SYSTEM |
Here"s a short explanation of the configuration directives.
Sets a Unix domain socket. This value can either be set at compile time if a domain socket is found at configure. This ini setting is Unix only.
Pdo_mysql.debug boolean
Enables debugging for PDO_MYSQL. This setting is only available when PDO_MYSQL is compiled against mysqlnd and in PDO debug mode.
Than install them
rpm -Uvh remi-release-26.rpm
rpm -Uvh epel-release-6-8.noarch.rpm
Know you can use remi repository to gest php-pdo and php-mysql.
yum --enablerepo=remi install php-pdo
yum --enablerepo=remi install php-mysql
Restart the Apache
systemctl stop httpd
systemctl start httpd
Good to go!
10 years ago
SQLSTATE: General error: 2014 Cannot execute queries while other unbuffered queries are active. ...
This one can be a royal pain to deal with. Never stack statements to be executed in one go. Nobody ever mentions this possibility in all the posts I"ve seen dealing with this error.
This example is a Zend Framework example but the theory is the same.
$sql
= <<<____SQL
`tid` int(11) NOT NULL,
`tgen` datetime NOT NULL,
`tterm` datetime,
`rqid` int(11) NOT NULL,
`rqtid` int(11) NOT NULL,
`rqsid` int(11) NOT NULL,
`rqdate` datetime NOT NULL,
`rssid` int(11) NOT NULL,
`rsdate` datetime,
`rscode` tinyint(1)
`rqid` int(5) NOT NULL,
`sid` int(11) NOT NULL,
`rlsid` int(11) NOT NULL,
`dcode` varchar(5) NOT NULL
____SQL;
$result
=
$this
->
db
->
getConnection
()->
exec
($sql
);
?>
This will run fine but PDO will balk with the "unbuffered" error if you follow this with another query.
$sql
= <<<____SQL
CREATE TABLE IF NOT EXISTS `ticket_hist` (
`tid` int(11) NOT NULL,
`trqform` varchar(40) NOT NULL,
`trsform` varchar(40) NOT NULL,
`tgen` datetime NOT NULL,
`tterm` datetime,
`tstatus` tinyint(1) NOT NULL
) ENGINE=ARCHIVE COMMENT="ticket archive";
____SQL;
$result
=
$this
->
db
->
getConnection
()->
exec
($sql
);
$sql
= <<<____SQL
CREATE TABLE IF NOT EXISTS `request_hist` (
`rqid` int(11) NOT NULL,
`rqtid` int(11) NOT NULL,
`rqsid` int(11) NOT NULL,
`rqdate` datetime NOT NULL,
`rqcode` tinyint(1) NOT NULL,
`rssid` int(11) NOT NULL,
`rsdate` datetime,
`rscode` tinyint(1)
) ENGINE=ARCHIVE COMMENT="request archive";
____SQL;
$result
=
$this
->
db
->
getConnection
()->
exec
($sql
);
$sql
= <<<____SQL
CREATE TABLE IF NOT EXISTS `relay_hist` (
`rqid` int(5) NOT NULL,
`sdesc` varchar(40) NOT NULL,
`rqemail` varchar(40) NOT NULL,
`sid` int(11) NOT NULL,
`rlsid` int(11) NOT NULL,
`dcode` varchar(5) NOT NULL
) ENGINE=ARCHIVE COMMENT="relay archive";
____SQL;
$result
=
$this
->
db
->
getConnection
()->
exec
($sql
);
?>
Chopping it into individual queries fixes the problem.
Множество PHP-разработчиков привыкли использовать для работы с базами данных расширения mysql и mysqli. Но с версии 5.1 в PHP существует более удобный способ - PHP Data Objects . Этот класс, сокращенно именуемый PDO, предоставляет методы для работы с объектами и prepared statements , которые заметно повысят вашу продуктивность!
«PDO – PHP Data Objects – это прослойка, которая предлагает универсальный способ работы с несколькими базами данных.»
$DBH расшифровывается как «database handle» и будет использоваться на протяжении всей статьи.
Закрыть любое подключение можно путем переопределения его переменной в null.
# закрывает подключение
$DBH = null;
Больше информации по теме отличительных опций разных СУБД и методах подключения к ним можно найти на php.net .
Использование prepared statements укрепляет защиту от SQL-инъекций.
Ниже находятся три примера prepared statements.
# без placeholders - дверь SQL-инъекциям открыта!
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ($name, $addr, $city)");
# безымянные placeholders
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)");
# именные placeholders
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");
Первый пример здесь лишь для сравнения, его стоит избегать. Разница между безымянными и именными placeholder’ами в том, как вы будете передавать данные в prepared statements.
Если в вашем SQL-выражении много параметров, то назначать каждому по переменной весьма неудобно. В таких случаях можно хранить данные в массиве и передавать его:
# набор данных, которые мы будем вставлять
$data = array("Cathy", "9 Dark and Twisty Road", "Cardiff");
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)");
$STH->execute($data);
$data вставится на место первого placeholder’а, $data - на место второго, и т.д. Но будьте внимательны: если ваши индексы сбиты, это работать не будет.
Если ваши данные нуждаются в обязательной обработке сразу после их получения из базы данных, ее можно реализовать в конструкторе класса.
Для примера возьмем ситуацию, когда вам нужно скрыть часть адреса проживания человека.
class secret_person {
public $name;
public $addr;
public $city;
public $other_data;
function __construct($other = "") {
$this->addr = preg_replace("//", "x", $this->addr);
$this->other_data = $other;
}
}
При создании объекта все латинские буквы в нижнем регистре должны замениться на x. Проверим:
$STH = $DBH->query("SELECT name, addr, city from folks");
$STH->setFetchMode(PDO::FETCH_CLASS, "secret_person");
while($obj = $STH->fetch()) {
echo $obj->addr;
}
Если в базе данных адрес выглядит как ’5 Rosebud’, то на выходе получится ’5 Rxxxxxx’.
Конечно, иногда будет требоваться, чтобы конструктор вызывался ПЕРЕД присваиванием значений. PDO такое тоже позволяет.
$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "secret_person");
Теперь, когда вы дополнили предыдущий пример дополнительной опцией (PDO::FETCH_PROPS_LATE), адрес видоизменяться не будет, так как после записи значений ничего не происходит.
Наконец, при необходимости можно передавать конструктору аргументы прямо при создании объекта:
$STH->setFetchMode(PDO::FETCH_CLASS, "secret_person", array("stuff"));
Можно даже передавать разные аргументы каждому объекту:
$i = 0;
while($rowObj = $STH->fetch(PDO::FETCH_CLASS, "secret_person", array($i))) {
// что-то делаем
$i++;
}
Термин PDO является сокращением понятия PHP Data Objects . Как можно судить по названию, эта технология позволяет работать с содержимым базы данных через объекты.
Чаще всего, в отношении новых технологий, встает вопрос их преимуществ перед старыми-добрыми и проверенными инструментами, а также, перевода на них текущих и старых проектов.
PHP развивается очень активно и стремится стать одним из лучших инструментов для быстрой разработки веб приложений как массового, так и корпоративного уровня.
Говоря о PHP , будем подразумевать современный объектно-ориентированный PHP , позволяющий писать универсальный код, удобный для тестирования и повторного использования.
Использование PDO позволяет вынести работу с базой данных на объектно-ориентированный уровень и улучшить переносимость кода. На самом деле, использование PDO не так сложно, как можно было бы подумать.
Представим, что мы уже продолжительное время разрабатываем приложение, с использованием MySQL . И вот, в один прекрасный момент, появляется необходимость заменить MySQL на PostgreSQL .
Как минимум, нам придется заменить все вызовы mysqli_connect() (mysql_connect()) на pg_connect() и, по аналогии, другие функции, используемые для запроса и обработки данных.
При использовании PDO , мы ограничимся изменением нескольких параметров в файлах конфигурации.
Использование связанных параметров предоставляет большую гибкость в составлении запросов и позволяет улучшить защиту от SQL инъекций.
Те, кто уже использует ORM (object-relational mapping — объектно-реляционное отображение данных), например, Doctrine , знают удобство представления данных из таблиц БД в виде объектов. PDO позволяет получать данные в виде объектов и без использования ORM .
Поддержка расширения mysql окончательно удалена из нового PHP 7 . Если вы планируете переносить проект на новую версию PHP , уже сейчас следует использовать в нем, как минимум, mysqli. Конечно же, лучше начинать использовать PDO , если вы еще не сделали этого.
Мне кажется, что этих причин достаточно для склонения весов в сторону использования PDO . Тем более, не нужно ничего дополнительно устанавливать.
Версии PHP 5.5 и выше, чаще всего, уже содержать расширение для работы с PDO . Для проверки достаточно выполнить в консоли простую команду:
php -i | grep "pdo"
Теперь откроем его в любом браузере и найдем нужные данные поиском по строке PDO .
Процесс работы с PDO не слишком отличается от традиционного. В общем случае, процесс использования PDO выглядит так:
Для подключения к базе данных нужно создать новый объект PDO и передать ему имя источника данных, так же известного как DSN .
В общем случае, DSN состоит из имени драйвера, отделенного двоеточием от строки подключения, специфичной для каждого драйвера PDO .
Для MySQL , подключение выполняется так:
$connection = new PDO("mysql:host=localhost;dbname=mydb;charset=utf8", "root", "root");
$connection = new PDO ("mysql:host=localhost;dbname=mydb;charset=utf8" , "root" , "root" ) ; |
В данном случае, DSN содержит имя драйвера mysql , указание хоста (возможен формат host=ИМЯ_ХОСТА:ПОРТ ), имя базы данных, кодировка, имя пользователя MySQL и его пароль.
В отличие от mysqli_query() , в PDO есть два типа запросов:
Первым делом, рассмотрим второй вариант.
Рассмотрим пример выполнения запроса на примере insert .
$connection->exec("INSERT INTO users VALUES (1, "somevalue"");
$connection -> exec () ; |
Конечно же, данный запрос возвращает количество затронутых строк и увидеть его можно следующим образом.
$affectedRows = $connection->exec("INSERT INTO users VALUES (1, "somevalue""); echo $affectedRows;
$affectedRows = $connection -> exec ("INSERT INTO users VALUES (1, "somevalue"" ) ; echo $affectedRows ; |
В случае использования mysqli_query () , код мог бы быть следующим.
$result = mysql_query("SELECT * FROM users"); while($row = mysql_fetch_assoc($result)) { echo $row["id"] . " " . $row["name"]; }
$result = mysql_query ("SELECT * FROM users" ) ; while ($row = mysql_fetch_assoc ($result ) ) { |
Для PDO , код будет проще и лаконичнее.
foreach($connection->query("SELECT * FROM users") as $row) { echo $row["id"] . " " . $row["name"]; }
foreach ($connection -> query ("SELECT * FROM users" ) as $row ) { echo $row [ "id" ] . " " . $row [ "name" ] ; |
Как и в mysqli , PDO позволяет получать данные в разных режимах. Для определения режима, класс PDO содержит соответствующие константы.
Примечание : это не полный список, все возможные константы и варианты их комбинации доступны в документации .
Пример получения ассоциативного массива:
$statement = $connection->query("SELECT * FROM users"); while($row = $statement->fetch(PDO::FETCH_ASSOC)) { echo $row["id"] . " " . $row["name"]; }
$statement = $connection -> while ($row = $statement -> fetch (PDO:: FETCH_ASSOC ) ) { echo $row [ "id" ] . " " . $row [ "name" ] ; |
Примечание : Рекомендуется всегда указывать режим выборки, так как режим PDO:: FETCH_BOTH потребует вдвое больше памяти — фактически, будут созданы два массива, ассоциативный и обычный.
Рассмотрим использование режима выборки PDO:: FETCH_CLASS . Создадим класс User :
class User { protected $id; protected $name; public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } }
class User protected $id ; protected $name ; public function getId () return $this -> id ; public function setId ($id ) $this -> id = $id ; public function getName () return $this -> name ; public function setName ($name ) $this -> name = $name ; |
Теперь выберем данные и отобразим данные при помощи методов класса:
$statement = $connection->query("SELECT * FROM users"); while($row = $statement->fetch(PDO::FETCH_CLASS, "User")) { echo $row->getId() . " " . $row->getName(); }
$statement = $connection -> query ("SELECT * FROM users" ) ; while ($row = $statement -> fetch (PDO:: FETCH_CLASS , "User" ) ) { echo $row -> getId () . " " . $row -> getName () ; |
Для понимания сути и всех преимуществ связывания параметров нужно более подробно рассмотреть механизмы PDO . При вызове $statement -> query () в коде выше, PDO подготовит запрос, выполнит его и вернет результат.
При вызове $connection -> prepare () создается подготовленный запрос. Подготовленные запросы — это способность системы управления базами данных получить шаблон запроса, скомпилировать его и выполнить после получения значений переменных, использованных в шаблоне. Похожим образом работают шаблонизаторы Smarty и Twig .
При вызове $statement -> execute () передаются значения для подстановки в шаблон запроса и СУБД выполняет запрос. Это действие аналогично вызову функции шаблонизатора render () .
Пример использования подготовленных запросов в PHP PDO :
В коде выше подготовлен запрос выборки записи с полем id равным значению, которое будет подставлено вместо : id . На данном этапе СУБД выполнит анализ и компиляцию запроса, возможно с использованием кеширования (зависит от настроек).
Теперь нужно передать недостающий параметр и выполнить запрос:
$id = 5; $statement->execute([ ":id" => $id ]);
Возможно, после рассмотрения механизма работы подготовленных запросов и связанных параметров, преимущества их использования стали очевидными.
PDO предоставляет удобную возможность экранирования пользовательских данных, например, такой код больше не нужен:
Вместо этого, теперь целесообразно делать так:
Можно, даже, еще укоротить код, используя нумерованные параметры вместо именованных:
В тоже время, использование подготовленных запросов позволяет улучшить производительность при многократном использовании запроса по одному шаблону. Пример выборки пяти случайных пользователей из базы данных:
$numberOfUsers = $connection->query("SELECT COUNT(*) FROM users")->fetchColumn(); $users = ; $statement = $connection->prepare("SELECT * FROM users WHERE id = ? LIMIT 1"); for ($i = 1; $i <= 5; $i++) { $id = rand(1, $numberOfUsers); $users = $statement->execute([$id])->fetch(PDO::FETCH_OBJ); }
$numberOfUsers = $connection -> query ("SELECT COUNT(*) FROM users" ) -> fetchColumn () ; $users = ; for ($i = 1 ; $i <= 5 ; $i ++ ) { $id = rand (1 , $numberOfUsers ) ; $users = $statement -> execute ([ $id ] ) -> fetch (PDO:: FETCH_OBJ ) ; |
При вызове метода prepare () , СУБД проведет анализ и скомпилирует запрос, при необходимости использует кеширование. Позже, в цикле for , происходит только выборка данных с указанным параметром. Такой подход позволяет быстрее получить данные, уменьшив время работы приложения.
При получении общего количества пользователей в базе данных был использован метод fetchColumn () . Этот метод позволяет получить значение одного столбца и является полезным при получении скалярных значений, таких как количество, сумма, максимально или минимальное значения.
Часто, при начале работы с PDO , возникают трудности с оператором IN . Например, представим, что пользователь вводит несколько имен, разделенных запятыми. Пользовательский ввод хранится в переменной $names .
Множество PHP-разработчиков привыкли использовать для работы с базами данных расширения mysql и mysqli. Но с версии 5.1 в PHP существует более удобный способ - PHP Data Objects . Этот класс, сокращенно именуемый PDO, предоставляет методы для работы с объектами и prepared statements , которые заметно повысят вашу продуктивность!
«PDO – PHP Data Objects – это прослойка, которая предлагает универсальный способ работы с несколькими базами данных.»
$DBH расшифровывается как «database handle» и будет использоваться на протяжении всей статьи.
Закрыть любое подключение можно путем переопределения его переменной в null.
# закрывает подключение
$DBH = null;
Больше информации по теме отличительных опций разных СУБД и методах подключения к ним можно найти на php.net .
Использование prepared statements укрепляет защиту от SQL-инъекций.
Ниже находятся три примера prepared statements.
# без placeholders - дверь SQL-инъекциям открыта!
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ($name, $addr, $city)");
# безымянные placeholders
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)");
# именные placeholders
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");
Первый пример здесь лишь для сравнения, его стоит избегать. Разница между безымянными и именными placeholder’ами в том, как вы будете передавать данные в prepared statements.
Если в вашем SQL-выражении много параметров, то назначать каждому по переменной весьма неудобно. В таких случаях можно хранить данные в массиве и передавать его:
# набор данных, которые мы будем вставлять
$data = array("Cathy", "9 Dark and Twisty Road", "Cardiff");
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)");
$STH->execute($data);
$data вставится на место первого placeholder’а, $data - на место второго, и т.д. Но будьте внимательны: если ваши индексы сбиты, это работать не будет.
Если ваши данные нуждаются в обязательной обработке сразу после их получения из базы данных, ее можно реализовать в конструкторе класса.
Для примера возьмем ситуацию, когда вам нужно скрыть часть адреса проживания человека.
class secret_person {
public $name;
public $addr;
public $city;
public $other_data;
function __construct($other = "") {
$this->addr = preg_replace("//", "x", $this->addr);
$this->other_data = $other;
}
}
При создании объекта все латинские буквы в нижнем регистре должны замениться на x. Проверим:
$STH = $DBH->query("SELECT name, addr, city from folks");
$STH->setFetchMode(PDO::FETCH_CLASS, "secret_person");
while($obj = $STH->fetch()) {
echo $obj->addr;
}
Если в базе данных адрес выглядит как ’5 Rosebud’, то на выходе получится ’5 Rxxxxxx’.
Конечно, иногда будет требоваться, чтобы конструктор вызывался ПЕРЕД присваиванием значений. PDO такое тоже позволяет.
$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "secret_person");
Теперь, когда вы дополнили предыдущий пример дополнительной опцией (PDO::FETCH_PROPS_LATE), адрес видоизменяться не будет, так как после записи значений ничего не происходит.
Наконец, при необходимости можно передавать конструктору аргументы прямо при создании объекта:
$STH->setFetchMode(PDO::FETCH_CLASS, "secret_person", array("stuff"));
Можно даже передавать разные аргументы каждому объекту:
$i = 0;
while($rowObj = $STH->fetch(PDO::FETCH_CLASS, "secret_person", array($i))) {
// что-то делаем
$i++;
}
Термин PDO является сокращением понятия PHP Data Objects . Как можно судить по названию, эта технология позволяет работать с содержимым базы данных через объекты.
Чаще всего, в отношении новых технологий, встает вопрос их преимуществ перед старыми-добрыми и проверенными инструментами, а также, перевода на них текущих и старых проектов.
PHP развивается очень активно и стремится стать одним из лучших инструментов для быстрой разработки веб приложений как массового, так и корпоративного уровня.
Говоря о PHP , будем подразумевать современный объектно-ориентированный PHP , позволяющий писать универсальный код, удобный для тестирования и повторного использования.
Использование PDO позволяет вынести работу с базой данных на объектно-ориентированный уровень и улучшить переносимость кода. На самом деле, использование PDO не так сложно, как можно было бы подумать.
Представим, что мы уже продолжительное время разрабатываем приложение, с использованием MySQL . И вот, в один прекрасный момент, появляется необходимость заменить MySQL на PostgreSQL .
Как минимум, нам придется заменить все вызовы mysqli_connect() (mysql_connect()) на pg_connect() и, по аналогии, другие функции, используемые для запроса и обработки данных.
При использовании PDO , мы ограничимся изменением нескольких параметров в файлах конфигурации.
Использование связанных параметров предоставляет большую гибкость в составлении запросов и позволяет улучшить защиту от SQL инъекций.
Те, кто уже использует ORM (object-relational mapping — объектно-реляционное отображение данных), например, Doctrine , знают удобство представления данных из таблиц БД в виде объектов. PDO позволяет получать данные в виде объектов и без использования ORM .
Поддержка расширения mysql окончательно удалена из нового PHP 7 . Если вы планируете переносить проект на новую версию PHP , уже сейчас следует использовать в нем, как минимум, mysqli. Конечно же, лучше начинать использовать PDO , если вы еще не сделали этого.
Мне кажется, что этих причин достаточно для склонения весов в сторону использования PDO . Тем более, не нужно ничего дополнительно устанавливать.
Версии PHP 5.5 и выше, чаще всего, уже содержать расширение для работы с PDO . Для проверки достаточно выполнить в консоли простую команду:
php -i | grep "pdo"
Теперь откроем его в любом браузере и найдем нужные данные поиском по строке PDO .
Процесс работы с PDO не слишком отличается от традиционного. В общем случае, процесс использования PDO выглядит так:
Для подключения к базе данных нужно создать новый объект PDO и передать ему имя источника данных, так же известного как DSN .
В общем случае, DSN состоит из имени драйвера, отделенного двоеточием от строки подключения, специфичной для каждого драйвера PDO .
Для MySQL , подключение выполняется так:
$connection = new PDO("mysql:host=localhost;dbname=mydb;charset=utf8", "root", "root");
$connection = new PDO ("mysql:host=localhost;dbname=mydb;charset=utf8" , "root" , "root" ) ; |
В данном случае, DSN содержит имя драйвера mysql , указание хоста (возможен формат host=ИМЯ_ХОСТА:ПОРТ ), имя базы данных, кодировка, имя пользователя MySQL и его пароль.
В отличие от mysqli_query() , в PDO есть два типа запросов:
Первым делом, рассмотрим второй вариант.
Рассмотрим пример выполнения запроса на примере insert .
$connection->exec("INSERT INTO users VALUES (1, "somevalue"");
$connection -> exec () ; |
Конечно же, данный запрос возвращает количество затронутых строк и увидеть его можно следующим образом.
$affectedRows = $connection->exec("INSERT INTO users VALUES (1, "somevalue""); echo $affectedRows;
$affectedRows = $connection -> exec ("INSERT INTO users VALUES (1, "somevalue"" ) ; echo $affectedRows ; |
В случае использования mysqli_query () , код мог бы быть следующим.
$result = mysql_query("SELECT * FROM users"); while($row = mysql_fetch_assoc($result)) { echo $row["id"] . " " . $row["name"]; }
$result = mysql_query ("SELECT * FROM users" ) ; while ($row = mysql_fetch_assoc ($result ) ) { |
Для PDO , код будет проще и лаконичнее.
foreach($connection->query("SELECT * FROM users") as $row) { echo $row["id"] . " " . $row["name"]; }
foreach ($connection -> query ("SELECT * FROM users" ) as $row ) { echo $row [ "id" ] . " " . $row [ "name" ] ; |
Как и в mysqli , PDO позволяет получать данные в разных режимах. Для определения режима, класс PDO содержит соответствующие константы.
Примечание : это не полный список, все возможные константы и варианты их комбинации доступны в документации .
Пример получения ассоциативного массива:
$statement = $connection->query("SELECT * FROM users"); while($row = $statement->fetch(PDO::FETCH_ASSOC)) { echo $row["id"] . " " . $row["name"]; }
$statement = $connection -> while ($row = $statement -> fetch (PDO:: FETCH_ASSOC ) ) { echo $row [ "id" ] . " " . $row [ "name" ] ; |
Примечание : Рекомендуется всегда указывать режим выборки, так как режим PDO:: FETCH_BOTH потребует вдвое больше памяти — фактически, будут созданы два массива, ассоциативный и обычный.
Рассмотрим использование режима выборки PDO:: FETCH_CLASS . Создадим класс User :
class User { protected $id; protected $name; public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } }
class User protected $id ; protected $name ; public function getId () return $this -> id ; public function setId ($id ) $this -> id = $id ; public function getName () return $this -> name ; public function setName ($name ) $this -> name = $name ; |
Теперь выберем данные и отобразим данные при помощи методов класса:
$statement = $connection->query("SELECT * FROM users"); while($row = $statement->fetch(PDO::FETCH_CLASS, "User")) { echo $row->getId() . " " . $row->getName(); }
$statement = $connection -> query ("SELECT * FROM users" ) ; while ($row = $statement -> fetch (PDO:: FETCH_CLASS , "User" ) ) { echo $row -> getId () . " " . $row -> getName () ; |
Для понимания сути и всех преимуществ связывания параметров нужно более подробно рассмотреть механизмы PDO . При вызове $statement -> query () в коде выше, PDO подготовит запрос, выполнит его и вернет результат.
При вызове $connection -> prepare () создается подготовленный запрос. Подготовленные запросы — это способность системы управления базами данных получить шаблон запроса, скомпилировать его и выполнить после получения значений переменных, использованных в шаблоне. Похожим образом работают шаблонизаторы Smarty и Twig .
При вызове $statement -> execute () передаются значения для подстановки в шаблон запроса и СУБД выполняет запрос. Это действие аналогично вызову функции шаблонизатора render () .
Пример использования подготовленных запросов в PHP PDO :
В коде выше подготовлен запрос выборки записи с полем id равным значению, которое будет подставлено вместо : id . На данном этапе СУБД выполнит анализ и компиляцию запроса, возможно с использованием кеширования (зависит от настроек).
Теперь нужно передать недостающий параметр и выполнить запрос:
$id = 5; $statement->execute([ ":id" => $id ]);
Возможно, после рассмотрения механизма работы подготовленных запросов и связанных параметров, преимущества их использования стали очевидными.
PDO предоставляет удобную возможность экранирования пользовательских данных, например, такой код больше не нужен:
Вместо этого, теперь целесообразно делать так:
Можно, даже, еще укоротить код, используя нумерованные параметры вместо именованных:
В тоже время, использование подготовленных запросов позволяет улучшить производительность при многократном использовании запроса по одному шаблону. Пример выборки пяти случайных пользователей из базы данных:
$numberOfUsers = $connection->query("SELECT COUNT(*) FROM users")->fetchColumn(); $users = ; $statement = $connection->prepare("SELECT * FROM users WHERE id = ? LIMIT 1"); for ($i = 1; $i <= 5; $i++) { $id = rand(1, $numberOfUsers); $users = $statement->execute([$id])->fetch(PDO::FETCH_OBJ); }
$numberOfUsers = $connection -> query ("SELECT COUNT(*) FROM users" ) -> fetchColumn () ; $users = ; for ($i = 1 ; $i <= 5 ; $i ++ ) { $id = rand (1 , $numberOfUsers ) ; $users = $statement -> execute ([ $id ] ) -> fetch (PDO:: FETCH_OBJ ) ; |
При вызове метода prepare () , СУБД проведет анализ и скомпилирует запрос, при необходимости использует кеширование. Позже, в цикле for , происходит только выборка данных с указанным параметром. Такой подход позволяет быстрее получить данные, уменьшив время работы приложения.
При получении общего количества пользователей в базе данных был использован метод fetchColumn () . Этот метод позволяет получить значение одного столбца и является полезным при получении скалярных значений, таких как количество, сумма, максимально или минимальное значения.
Часто, при начале работы с PDO , возникают трудности с оператором IN . Например, представим, что пользователь вводит несколько имен, разделенных запятыми. Пользовательский ввод хранится в переменной $names .