Ошибки и их обработка
PDO предлагает на выбор 3 стратегии обработки ошибок в зависимости от вашего
стиля разработки приложений.
-
PDO::ERRMODE_SILENTДо PHP 8.0.0, это был режим по умолчанию. PDO просто предоставит вам код ошибки, который
можно получить методами PDO::errorCode() и
PDO::errorInfo(). Эти методы реализованы как в объектах
запросов, так и в объектах баз данных. Если ошибка вызвана во время выполнения
кода объекта запроса, нужно вызвать метод
PDOStatement::errorCode() или
PDOStatement::errorInfo() этого объекта. Если ошибка
вызова объекта базы данных, нужно вызвать аналогичные методы у этого объекта. -
PDO::ERRMODE_WARNINGПомимо установки кода ошибки PDO выдаст обычное E_WARNING сообщение. Это может
быть полезно при отладке или тестировании, когда нужно видеть, что произошло,
но не нужно прерывать работу приложения. -
PDO::ERRMODE_EXCEPTIONНачиная с PHP 8.0.0 является режимом по умолчанию. Помимо задания кода ошибки PDO будет выбрасывать исключение
PDOException, свойства которого будут отражать
код ошибки и её описание. Этот режим также полезен при отладке, так как
сразу известно, где в программе произошла ошибка. Это позволяет быстро
локализовать и решить проблему. (Не забывайте, что если исключение
является причиной завершения работы скрипта, все активные транзакции
будут откачены.)Режим исключений также полезен, так как даёт возможность структурировать
обработку ошибок более тщательно, нежели с обычными предупреждениями PHP, а
также с меньшей вложенностью кода, чем в случае работы в тихом режиме с
явной проверкой возвращаемых значений при каждом обращении к базе данных.Подробнее об исключениях в PHP смотрите в разделе Исключения.
PDO стандартизирован для работы со строковыми кодами ошибок SQL-92 SQLSTATE.
Отдельные драйверы PDO могут задавать соответствия своих собственных кодов
кодам SQLSTATE. Метод PDO::errorCode() возвращает одиночный
код SQLSTATE. Если необходима специфичная информация об ошибке, PDO предлагает
метод PDO::errorInfo(), который возвращает массив, содержащий
код SQLSTATE, код ошибки драйвера, а также строку ошибки драйвера.
Пример #1 Создание PDO объекта и установка режима обработки ошибок
<?php
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);// PDO выбросит исключение PDOException (если таблица не существует)
$dbh->query("SELECT wrongcolumn FROM wrongtable");
?>
Результат выполнения данного примера:
Fatal error: Uncaught PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'testdb.wrongtable' doesn't exist in /tmp/pdo_test.php:10
Stack trace:
#0 /tmp/pdo_test.php(10): PDO->query('SELECT wrongcol...')
#1 {main}
thrown in /tmp/pdo_test.php on line 10
Замечание:
Метод PDO::__construct() будет всегда бросать исключение PDOException,
если соединение оборвалось, независимо от установленного значенияPDO::ATTR_ERRMODE.
Пример #2 Создание экземпляра класса PDO и установка режима обработки ошибок в конструкторе
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'googleguy';
$password = 'googleguy';$dbh = new PDO($dsn, $user, $password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));// Следующий запрос приводит к ошибке уровня E_WARNING вместо исключения (когда таблица не существует)
$dbh->query("SELECT wrongcolumn FROM wrongtable");
?>
Результат выполнения данного примера:
Warning: PDO::query(): SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.wrongtable' doesn't exist in /tmp/pdo_test.php on line 9
There are no user contributed notes for this page.
Вернуться к: PDO
PDO предлагает на выбор 3 стратегии обработки ошибок в зависимости от вашего
стиля разработки приложений.
-
PDO::ERRMODE_SILENTЭто режим по умолчанию. PDO просто предоставит вам код ошибки, который
можно получить методами PDO::errorCode() и
PDO::errorInfo(). Эти методы реализованы как в объектах
запросов так и в объектах баз данных. Если ошибка вызвана во время выполнения
кода объекта запроса, нужно вызвать метод
PDOStatement::errorCode() или
PDOStatement::errorInfo() этого объекта. Если ошибка
вызова объекта базы данных, нужно вызвать аналогичные методы у этого объекта. -
PDO::ERRMODE_WARNINGПомимо задания кода ошибки PDO выдаст обычное E_WARNING сообщение. Это может
быть полезно при отладке или тестировании, когда нужно видеть, что произошло,
но не нужно прерывать работу приложения. -
PDO::ERRMODE_EXCEPTIONПомимо задания кода ошибки PDO будет выбрасывать исключение
PDOException, свойства которого будут отражать
код ошибки и ее описание. Этот режим также полезен при отладке, так как
сразу известно, где в программе произошла ошибка. Это позволяет быстро
локализовать и решить проблему. (Не забывайте, что если исключение
является причиной завершения работы скрипта, все активные транзакции
будут откачены.)Режим исключений также полезен, так как дает возможность структурировать
обработку ошибок более тщательно, нежели с обычными PHP предупреждениями, а
также с меньшей вложенностью кода, чем в случае работы в тихом режиме с
явной проверкой возвращаемых значений при каждом обращении к базе данных.Подробнее об исключениях в PHP см. в разделе Исключения.
PDO стандартизирован для работы со строковыми кодами ошибок SQL-92 SQLSTATE.
Отдельные PDO драйверы могут задавать соответствия своих собственных кодов
кодам SQLSTATE. Метод PDO::errorCode() возвращает одиночный
SQLSTATE код. Если необходима специфичная информация об ошибке, PDO предлагает
метод PDO::errorInfo(), который возвращает массив, содержащий
SQLSTATE код, код ошибки драйвера, а также строку ошибки драйвера.
Пример #1 Создание PDO объекта и задание режима обработки ошибок
<?php
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Подключение не удалось: ' . $e->getMessage();
}?>
Замечание:
Метод PDO::__construct() будет всегда бросать исключение PDOException,
если соединение оборвалось, независимо от установленного значенияPDO::ATTR_ERRMODE.
Непойманные исключения фатальны.
Пример #2 Создание экземпляра класса PDO и задание режима обработки ошибок в конструкторе
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'googleguy';
$password = 'googleguy';/*
По прежнему оберните конструктор в блок try/catch, так как, даже при установке ERRMODE в WARNING,
PDO::__construct всегда будет бросать исключение PDOException, если соединение оборвалось.
*/
try {
$dbh = new PDO($dsn, $user, $password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
} catch (PDOException $e) {
echo 'Соединение оборвалось: ' . $e->getMessage();
exit;
}// Следующий запрос приводит к ошибке уровня E_WARNING вместо исключения (когда таблица не существует)
$dbh->query("SELECT wrongcolumn FROM wrongtable");
?>
Результат выполнения данного примера:
Warning: PDO::query(): SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.wrongtable' doesn't exist in /tmp/pdo_test.php on line 18
Вернуться к: PDO
Содержание
- Ошибки и их обработка
- Ошибки и их обработка
- PDO::__construct
- Description
- Parameters
- Errors/Exceptions
- Examples
- User Contributed Notes 14 notes
- Почему стоит пользоваться PDO для работы с базой данных
- Введение в PDO
- Поддержка СУБД
- Подключение
- Исключения и PDO
- PDO::ERRMODE_SILENT
- PDO::ERRMODE_WARNING
- PDO::ERRMODE_EXCEPTION
- Insert и Update
- Prepared Statements
- Безымянные placeholder’ы
- Именные placeholder’ы
- Выборка данных
- FETCH_ASSOC
- FETCH_OBJ
- FETCH_CLASS
- Другие полезные методы
Ошибки и их обработка
PDO предлагает на выбор 3 стратегии обработки ошибок в зависимости от вашего стиля разработки приложений.
PDO::ERRMODE_SILENT
Это режим по умолчанию. PDO просто предоставит вам код ошибки, который можно получить методами PDO::errorCode() и PDO::errorInfo() . Эти методы реализованы как в объектах запросов так и в объектах баз данных. Если ошибка вызвана во время выполнения кода объекта запроса, нужно вызвать метод PDOStatement::errorCode() или PDOStatement::errorInfo() этого объекта. Если ошибка вызова объекта базы данных, нужно вызвать аналогичные методы у этого объекта.
PDO::ERRMODE_WARNING
Помимо задания кода ошибки PDO выдаст обычное E_WARNING сообщение. Это может быть полезно при отладке или тестировании, когда нужно видеть, что произошло, но не нужно прерывать работу приложения.
PDO::ERRMODE_EXCEPTION
Помимо задания кода ошибки PDO будет выбрасывать исключение PDOException, свойства которого будут отражать код ошибки и ее описание. Этот режим также полезен при отладке, так как сразу известно, где в программе произошла ошибка. Это позволяет быстро локализовать и решить проблему. (Не забывайте, что если исключение является причиной завершения работы скрипта, все активные транзакции будут откачены.)
Режим исключений также полезен, так как дает возможность структурировать обработку ошибок более тщательно, нежели с обычными PHP предупреждениями, а также с меньшей вложенностью кода, чем в случае работы в тихом режиме с явной проверкой возвращаемых значений при каждом обращении к базе данных.
Подробнее об исключениях в PHP см. в разделе Исключения.
PDO стандартизирован для работы со строковыми кодами ошибок SQL-92 SQLSTATE. Отдельные PDO драйверы могут задавать соответствия своих собственных кодов кодам SQLSTATE. Метод PDO::errorCode() возвращает одиночный SQLSTATE код. Если необходима специфичная информация об ошибке, PDO предлагает метод PDO::errorInfo() , который возвращает массив, содержащий SQLSTATE код, код ошибки драйвера, а также строку ошибки драйвера.
Пример #1 Создание PDO объекта и задание режима обработки ошибок
= ‘mysql:dbname=testdb;host=127.0.0.1’ ;
$user = ‘dbuser’ ;
$password = ‘dbpass’ ;
try <
$dbh = new PDO ( $dsn , $user , $password );
$dbh -> setAttribute ( PDO :: ATTR_ERRMODE , PDO :: ERRMODE_EXCEPTION );
> catch ( PDOException $e ) <
echo ‘Подключение не удалось: ‘ . $e -> getMessage ();
>
Метод PDO::__construct() будет всегда бросать исключение PDOException, если соединение оборвалось, независимо от установленного значения PDO::ATTR_ERRMODE . Непойманные исключения фатальны.
Пример #2 Создание экземпляра класса PDO и задание режима обработки ошибок в конструкторе
= ‘mysql:dbname=test;host=127.0.0.1’ ;
$user = ‘googleguy’ ;
$password = ‘googleguy’ ;
/*
По прежнему оберните конструктор в блок try/catch, так как, даже при установке ERRMODE в WARNING,
PDO::__construct всегда будет бросать исключение PDOException, если соединение оборвалось.
*/
try <
$dbh = new PDO ( $dsn , $user , $password , array( PDO :: ATTR_ERRMODE => PDO :: ERRMODE_WARNING ));
> catch ( PDOException $e ) <
echo ‘Соединение оборвалось: ‘ . $e -> getMessage ();
exit;
>
// Следующий запрос приводит к ошибке уровня E_WARNING вместо исключения (когда таблица не существует)
$dbh -> query ( «SELECT wrongcolumn FROM wrongtable» );
?>
Источник
Ошибки и их обработка
PDO предлагает на выбор 3 стратегии обработки ошибок в зависимости от вашего стиля разработки приложений.
PDO::ERRMODE_SILENT
До PHP 8.0.0, это был режим по умолчанию. PDO просто предоставит вам код ошибки, который можно получить методами PDO::errorCode() и PDO::errorInfo() . Эти методы реализованы как в объектах запросов, так и в объектах баз данных. Если ошибка вызвана во время выполнения кода объекта запроса, нужно вызвать метод PDOStatement::errorCode() или PDOStatement::errorInfo() этого объекта. Если ошибка вызова объекта базы данных, нужно вызвать аналогичные методы у этого объекта.
PDO::ERRMODE_WARNING
Помимо установки кода ошибки PDO выдаст обычное E_WARNING сообщение. Это может быть полезно при отладке или тестировании, когда нужно видеть, что произошло, но не нужно прерывать работу приложения.
PDO::ERRMODE_EXCEPTION
Начиная с PHP 8.0.0 является режимом по умолчанию. Помимо задания кода ошибки PDO будет выбрасывать исключение PDOException , свойства которого будут отражать код ошибки и её описание. Этот режим также полезен при отладке, так как сразу известно, где в программе произошла ошибка. Это позволяет быстро локализовать и решить проблему. (Не забывайте, что если исключение является причиной завершения работы скрипта, все активные транзакции будут откачены.)
Режим исключений также полезен, так как даёт возможность структурировать обработку ошибок более тщательно, нежели с обычными предупреждениями PHP, а также с меньшей вложенностью кода, чем в случае работы в тихом режиме с явной проверкой возвращаемых значений при каждом обращении к базе данных.
Подробнее об исключениях в PHP смотрите в разделе Исключения.
PDO стандартизирован для работы со строковыми кодами ошибок SQL-92 SQLSTATE. Отдельные драйверы PDO могут задавать соответствия своих собственных кодов кодам SQLSTATE. Метод PDO::errorCode() возвращает одиночный код SQLSTATE. Если необходима специфичная информация об ошибке, PDO предлагает метод PDO::errorInfo() , который возвращает массив, содержащий код SQLSTATE, код ошибки драйвера, а также строку ошибки драйвера.
Пример #1 Создание PDO объекта и установка режима обработки ошибок
= ‘mysql:dbname=testdb;host=127.0.0.1’ ;
$user = ‘dbuser’ ;
$password = ‘dbpass’ ;
$dbh = new PDO ( $dsn , $user , $password );
$dbh -> setAttribute ( PDO :: ATTR_ERRMODE , PDO :: ERRMODE_EXCEPTION );
// PDO выбросит исключение PDOException (если таблица не существует)
$dbh -> query ( «SELECT wrongcolumn FROM wrongtable» );
?>
Результат выполнения данного примера:
Метод PDO::__construct() будет всегда бросать исключение PDOException , если соединение оборвалось, независимо от установленного значения PDO::ATTR_ERRMODE .
Пример #2 Создание экземпляра класса PDO и установка режима обработки ошибок в конструкторе
= ‘mysql:dbname=test;host=127.0.0.1’ ;
$user = ‘googleguy’ ;
$password = ‘googleguy’ ;
$dbh = new PDO ( $dsn , $user , $password , array( PDO :: ATTR_ERRMODE => PDO :: ERRMODE_WARNING ));
// Следующий запрос приводит к ошибке уровня E_WARNING вместо исключения (когда таблица не существует)
$dbh -> query ( «SELECT wrongcolumn FROM wrongtable» );
?>
Источник
PDO::__construct
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDO::__construct — Creates a PDO instance representing a connection to a database
Description
Creates a PDO instance to represent a connection to the requested database.
Parameters
The Data Source Name, or DSN, contains the information required to connect to the database.
In general, a DSN consists of the PDO driver name, followed by a colon, followed by the PDO driver-specific connection syntax. Further information is available from the PDO driver-specific documentation.
The dsn parameter supports three different methods of specifying the arguments required to create a database connection:
dsn contains the full DSN.
dsn consists of uri: followed by a URI that defines the location of a file containing the DSN string. The URI can specify a local file or a remote URL.
dsn consists of a name name that maps to pdo.dsn. name in php.ini defining the DSN string.
The alias must be defined in php.ini , and not .htaccess or httpd.conf
The user name for the DSN string. This parameter is optional for some PDO drivers.
The password for the DSN string. This parameter is optional for some PDO drivers.
A key=>value array of driver-specific connection options.
Errors/Exceptions
PDO::__construct() throws a PDOException if the attempt to connect to the requested database fails, regardless of which PDO::ATTR_ERRMODE is currently set.
Examples
Example #1 Create a PDO instance via driver invocation
/* Connect to a MySQL database using driver invocation */
$dsn = ‘mysql:dbname=testdb;host=127.0.0.1’ ;
$user = ‘dbuser’ ;
$password = ‘dbpass’ ;
$dbh = new PDO ( $dsn , $user , $password );
Example #2 Create a PDO instance via URI invocation
The following example assumes that the file /usr/local/dbconnect exists with file permissions that enable PHP to read the file. The file contains the PDO DSN to connect to a DB2 database through the PDO_ODBC driver:
The PHP script can then create a database connection by simply passing the uri: parameter and pointing to the file URI:
/* Connect to an ODBC database using driver invocation */
$dsn = ‘uri:file:///usr/local/dbconnect’ ;
$user = » ;
$password = » ;
$dbh = new PDO ( $dsn , $user , $password );
Example #3 Create a PDO instance using an alias
The following example assumes that php.ini contains the following entry to enable a connection to a MySQL database using only the alias mydb :
/* Connect to an ODBC database using an alias */
$dsn = ‘mydb’ ;
$user = » ;
$password = » ;
$dbh = new PDO ( $dsn , $user , $password );
User Contributed Notes 14 notes
To get UTF-8 charset you can specify that in the DSN.
$link = new PDO(«mysql:host=localhost;dbname=DB;charset=UTF8»);
To specify a database connection port use the following DSN string
Most of the information in the comment here is outdated or wrong.
You do can use host=localhost to connect via socket, which is faster than TCP, so setting 127.0.0.1 is a performance loss.
To use proper utf you should use utf8mb4, for example:
$db = new PDO(‘mysql:host=’ . DATABASE_HOST . ‘;dbname=’. DATABASE_NAME .’;charset=utf8mb4′, DATABASE_USER, DATABASE_PASSWORD);
If you use the UTF-8 encoding, you have to use the fourth parameter :
= new PDO ( ‘mysql:host=myhost;dbname=mydb’ , ‘login’ , ‘password’ , array( PDO :: MYSQL_ATTR_INIT_COMMAND => ‘SET NAMES ’UTF8» ));
?>
If you override PDO with own class and you want to implement alias from php.ini, you have to get it with `get_cfg_var` instead of `ini_get`.
class PDO extends PDO
<
public function __construct ( string $dsn , ? string $username = null , ? string $password = null , ?array $options = null )
<
//alias
if (! str_contains ( $dsn , ‘:’ )) <
$dsn = get_cfg_var ( ‘pdo.dsn.’ . $dsn );
if (! $dsn ) <
throw new PDOException ( ‘Argument #1 ($dsn) must be a valid data source name’ );
>
>
// your additional logic
parent :: __construct ( $dsn , $username , $password , $options );
>
>
?>
You will get a fatal error if you don’t catch the exception threw by PDO when it fails to connect to the database server like this.
Fatal error: in xxx.php on line xx
This error neither can be handled by error handlers nor can it be erased by the @ sign, which can make your script uncontrollable.
= new pdo ( ‘mysql:host=127.0.0.1;port=3306;dbname=mysql;charset=utf8’ , ‘user’ , ‘password’ ,array(
PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION ,
));
?>
You should always try . catch .
try <
$db = new pdo ( ‘mysql:host=127.0.0.1;port=3306;dbname=mysql;charset=utf8’ , ‘user’ , ‘password’ ,array(
PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION ,
));
>catch( PDOException $pe ) <
echo $pe -> getMessage ();
>
?>
try <
$pdo = new PDO ( ‘sqlite:example.db’ );
>catch ( PDOException $e ) <
die ( ‘DB Error’ );
>
?>
If ‘example.db’ does not exist, no exception is thrown but the file ‘example.db’ is created.
Although not explicitly stated, parameters in the PDO $dsn string may be case-sensitive on some platforms or drivers.
// The dbname will not be parsed with incorrect casing:
$pdo = new PDO ( «mysql:host=hostname;DBName=database» , «user» , «password» );
// The correct dbname is lowercase, as displayed in the manual:
$pdo = new PDO ( «mysql:host=hostname;dbname=database» , «user» , «password» );
?>
For IBM i / iSeries / AS400 DB2 connections, you can specify system naming and library lists for php_pdo modules > 1.3.3-sg2
The PDO connection is case-sensitive, this means that you cannot write
`$PDO = new PDO(«MySQL:DBName=dbname;host=localhost»);`
You would have to write it
`$PDO = new PDO(«mysql:dbname=dbname;host=localhost»);`
The difference here is that `mysql` and `dbname` is with all lower-case.
Some IDE’s like PHPStorm will show a `TYPO ERROR`, at `dbname` if it’s written with lower-case only, this is just to be ignored and have been reported to PHPStorm for them to fix. (Currrent version 10.0.2)
Источник
Почему стоит пользоваться PDO для работы с базой данных
Перевод статьи Why you Should be using PHP’s PDO for Database Access.
Множество PHP-разработчиков привыкли использовать для работы с базами данных расширения mysql и mysqli. Но с версии 5.1 в PHP существует более удобный способ — PHP Data Objects. Этот класс, сокращенно именуемый PDO, предоставляет методы для работы с объектами и prepared statements, которые заметно повысят вашу продуктивность!
Введение в PDO
«PDO – PHP Data Objects – это прослойка, которая предлагает универсальный способ работы с несколькими базами данных.»
Поддержка СУБД
Это расширение может поддерживать любую систему управления базами данных, для которой существует PDO-драйвер. На момент написания статьи доступны следующие драйвера:
- PDO_CUBRID ( CUBRID )
- PDO_DBLIB ( FreeTDS / Microsoft SQL Server / Sybase )
- PDO_FIREBIRD ( Firebird/Interbase 6 )
- PDO_IBM ( IBM DB2 )
- PDO_INFORMIX ( IBM Informix Dynamic Server )
- PDO_MYSQL ( MySQL 3.x/4.x/5.x )
- PDO_OCI ( Oracle Call Interface )
- PDO_ODBC ( ODBC v3 (IBM DB2, unixODBC and win32 ODBC) )
- PDO_PGSQL ( PostgreSQL )
- PDO_SQLITE ( SQLite 3 and SQLite 2 )
- PDO_SQLSRV ( Microsoft SQL Server )
- PDO_4D ( 4D )
Впрочем, не все из них есть на вашем сервере. Увидеть список доступных драйверов можно так:
Подключение
Способы подключения к разным СУБД могут незначительно отличаться. Ниже приведены примеры подключения к наиболее популярным из них. Можно заметить, что первые три имеют идентичный синтаксис, в отличие от SQLite.
Пожалуйста, обратите внимание на блок try/catch – всегда стоит оборачивать в него все свои PDO-операции и использовать механизм исключений (об этом чуть дальше).
$DBH расшифровывается как «database handle» и будет использоваться на протяжении всей статьи.
Закрыть любое подключение можно путем переопределения его переменной в null.
Больше информации по теме отличительных опций разных СУБД и методах подключения к ним можно найти на php.net.
Исключения и PDO
PDO умеет выбрасывать исключения при ошибках, поэтому все должно находиться в блоке try/catch. Сразу после создания подключения, PDO можно перевести в любой из трех режимов ошибок:
Но стоит заметить, что ошибка при попытке соединения будет всегда вызывать исключение.
PDO::ERRMODE_SILENT
Это режим по умолчанию. Примерно то же самое вы, скорее всего, используете для отлавливания ошибок в расширениях mysql и mysqli. Следующие два режима больше подходят для DRY программирования.
PDO::ERRMODE_WARNING
Этот режим вызовет стандартный Warning и позволит скрипту продолжить выполнение. Удобен при отладке.
PDO::ERRMODE_EXCEPTION
В большинстве ситуаций этот тип контроля выполнения скрипта предпочтителен. Он выбрасывает исключение, что позволяет вам ловко обрабатывать ошибки и скрывать щепетильную информацию. Как, например, тут:
В SQL-выражении есть синтаксическая ошибка, которая вызовет исключение. Мы можем записать детали ошибки в лог-файл и человеческим языком намекнуть пользователю, что что-то случилось.
Insert и Update
Вставка новых и обновление существующих данных являются одними из наиболее частых операций с БД. В случае с PDO этот процесс обычно состоит из двух шагов. (В следующей секции все относится как к UPDATE, так и INSERT)
Тривиальный пример вставки новых данных:
Вообще-то можно сделать то же самое одним методом exec(), но двухшаговый способ дает все преимущества prepared statements. Они помогают в защите от SQL-инъекций, поэтому имеет смысл их использовать даже при однократном запросе.
Prepared Statements
Использование prepared statements укрепляет защиту от SQL-инъекций.
Prepared statement — это заранее скомпилированное SQL-выражение, которое может быть многократно выполнено путем отправки серверу лишь различных наборов данных. Дополнительным преимуществом является невозможность провести SQL-инъекцию через данные, используемые в placeholder’ах.
Ниже находятся три примера prepared statements.
Первый пример здесь лишь для сравнения, его стоит избегать. Разница между безымянными и именными placeholder’ами в том, как вы будете передавать данные в prepared statements.
Безымянные placeholder’ы
Здесь два шага. На первом мы назначаем всем placeholder’ам переменные (строки 2-4). Затем назначаем этим переменным значения и выполняем запрос. Чтобы послать новый набор данных, просто измените значения переменных и выполните запрос еще раз.
Если в вашем SQL-выражении много параметров, то назначать каждому по переменной весьма неудобно. В таких случаях можно хранить данные в массиве и передавать его:
$data[0] вставится на место первого placeholder’а, $data[1] — на место второго, и т.д. Но будьте внимательны: если ваши индексы сбиты, это работать не будет.
Именные placeholder’ы
Здесь тоже можно передавать массив, но он должен быть ассоциативным. В роли ключей должны выступать, как можно догадаться, имена placeholder’ов.
Одним из удобств использования именных placeholder’ов является возможность вставки объектов напрямую в базу данных, если названия свойств совпадают с именами параметров. Вставку данных, к примеру, вы можете выполнить так:
Преобразование объекта в массив при execute() приводит к тому, что свойства считаются ключами массива.
Выборка данных

Данные можно получить с помощью метода ->fetch(). Перед его вызовом желательно явно указать, в каком виде они вам требуются. Есть несколько вариантов:
- PDO::FETCH_ASSOC: возвращает массив с названиями столбцов в виде ключей
- PDO::FETCH_BOTH (по умолчанию): возвращает массив с индексами как в виде названий стобцов, так и их порядковых номеров
- PDO::FETCH_BOUND: присваивает значения столбцов соответствующим переменным, заданным с помощью метода ->bindColumn()
- PDO::FETCH_CLASS: присваивает значения столбцов соответствующим свойствам указанного класса. Если для какого-то столбца свойства нет, оно будет создано
- PDO::FETCH_INTO: обновляет существующий экземпляр указанного класса
- PDO::FETCH_LAZY: объединяет в себе PDO::FETCH_BOTH и PDO::FETCH_OBJ
- PDO::FETCH_NUM: возвращает массив с ключами в виде порядковых номеров столбцов
- PDO::FETCH_OBJ: возвращает анонимный объект со свойствами, соответствующими именам столбцов
На практике вам обычно хватит трех: FETCH_ASSOC, FETCH_CLASS, и FETCH_OBJ. Чтобы задать формат данных, используется следующий синтаксис:
Также можно задать его напрямую при вызове метода ->fetch().
FETCH_ASSOC
При этом формате создается ассоциативный массив с названиями столбцов в виде индексов. Он должен быть знаком тем, кто использует расширения mysql/mysqli.
Цикл while() переберет весь результат запроса.
FETCH_OBJ
Данный тип получения данных создает экземпляр класса std для каждой строки.
FETCH_CLASS
При использовании fetch_class данные заносятся в экземпляры указанного класса. При этом значения назначаются свойствам объекта ДО вызова конструктора. Если свойства с именами, соответствующими названиям столбцов, не существуют, они будут созданы автоматически (с областью видимости public).
Если ваши данные нуждаются в обязательной обработке сразу после их получения из базы данных, ее можно реализовать в конструкторе класса.
Для примера возьмем ситуацию, когда вам нужно скрыть часть адреса проживания человека.
При создании объекта все латинские буквы в нижнем регистре должны замениться на x. Проверим:
Если в базе данных адрес выглядит как ’5 Rosebud’, то на выходе получится ’5 Rxxxxxx’.
Конечно, иногда будет требоваться, чтобы конструктор вызывался ПЕРЕД присваиванием значений. PDO такое тоже позволяет.
Теперь, когда вы дополнили предыдущий пример дополнительной опцией (PDO::FETCH_PROPS_LATE), адрес видоизменяться не будет, так как после записи значений ничего не происходит.
Наконец, при необходимости можно передавать конструктору аргументы прямо при создании объекта:
Можно даже передавать разные аргументы каждому объекту:
Другие полезные методы
Хотя эта статья не может (и не пытается) охватить все аспекты работы с PDO (это огромный модуль!), оставить без упоминания следующие несколько функций нельзя.
Метод ->lastInsertId() возвращает id последней вставленной записи. Стоит заметить, что он всегда вызывается у объекта базы данных (в статье он именуется $DBH), а не объекта с выражением ($STH).
Метод ->exec() используется для операций, которые не возвращают никаких данных, кроме количества затронутых ими записей.
Метод ->quote() ставит кавычки в строковых данных таким образом, что их становится безопасно использовать в запросах. Пригодится, если вы не используете prepared statements.
Метод ->rowCount() возвращает количество записей, которые поучаствовали в операции. К сожалению, эта функция отказывалась работать с SELECT-запросами вплоть до PHP 5.1.6. Если обновить версию PHP не представляется возможным, количество записей можно получить так:
Источник
PDO (PHP Data Objects) — расширение PHP, которое реализует взаимодействие с базами данных при помощи объектов. Профит в том, что отсутствует привязка к конкретной системе управления базами данных.
Предоставляемый интерфейс поддерживает, среди прочих, такие популярные СУБД:
- MySQL;
- SQLite;
- PostgreSQL;
- Microsoft SQL Server.
В этом руководстве представлен обзор PDO:
- Пошаговое описание работы с базами данных, начиная с установки соединения до выполнения выборки.
- Порядок использования подготовленных запросов.
- Настройка обработки ошибок.
Для работы потребуются:
- базовые знания MySQL и опыт использования команды
mysqlв консоли; - понимание основ объектно-ориентированного программирования;
- PHP >= 5.1;
- рабочая СУБД MySQL/MariaDB.
Создание тестовой базы данных и таблицы
Для начала создадим базу данных для этого руководства:
CREATE DATABASE solar_system;
GRANT ALL PRIVILEGES ON solar_system.* TO 'testuser'@'localhost'
IDENTIFIED BY 'testpassword';
Пользователю с логином testuser и паролем testpassword предоставили полные права доступа к базе solar_system.
Теперь создадим таблицу и заполним данными, астрономическая точность которых не подразумевается:
USE solar_system;
CREATE TABLE planets (
id TINYINT(1) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
name VARCHAR(10) NOT NULL,
color VARCHAR(10) NOT NULL
);
INSERT INTO planets(name, color) VALUES('earth', 'blue'), ('mars', 'red'), ('jupiter', 'strange');
Описание соединения
Теперь, когда создана база, определим DSN (Data Source Name) — сведения для подключения к базе, представленные в виде строки. Синтаксис описания отличается в зависимости от используемой СУБД. В примере работаем с MySQL/MariaDB, поэтому указываем:
- тип драйвера;
- имя хоста, где расположена СУБД;
- порт (необязательно, если используется стандартный порт
3306); - имя базы данных;
- кодировку (необязательно).
Строка DSN в этом случае выглядит следующим образом:
$dsn = "mysql:host=localhost;port=3306;dbname=solar_system;charset=utf8";
Первым указывается database prefix. В примере — mysql. Префикс отделяется от остальной части строки двоеточием, а каждый следующий параметр — точкой с запятой.
Теперь, когда строка DSN готова, создадим PDO-объект. Конструктор на входе принимает следующие параметры:
- Строку DSN.
- Имя пользователя, имеющего доступ к базе данных.
- Пароль этого пользователя.
- Массив с дополнительными параметрами (необязательно).
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
];
$pdo = new PDO($dsn, 'testuser', 'testpassword', $options);
Дополнительные параметры можно также определить после создания объекта с помощью метода SetAttribute:
$pdo->SetAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Определение метода выборки по умолчанию
PDO::DEFAULT_FETCH_MODE — важный параметр, который определяет метод выборки по умолчанию. Указанный метод используется при получении результата выполнения запроса.
PDO::FETCH_BOTH
Режим по умолчанию. Результат выборки индексируется как номерами (начиная с 0), так и именами столбцов:
$stmt = $pdo->query("SELECT * FROM planets");
$results = $stmt->fetch(PDO::FETCH_BOTH);
После выполнения запроса с этим режимом к тестовой таблице планет получим следующий результат:
Array
(
[id] => 1
[0] => 1
[name] => earth
[1] => earth
[color] => blue
[2] => blue
)
PDO::FETCH_ASSOC
Результат сохраняется в ассоциативном массиве, в котором ключ — имя столбца, а значение — соответствующее значение строки:
$stmt = $pdo->query("SELECT * FROM planets");
$results = $stmt->fetch(PDO::FETCH_ASSOC);
В результате получим:
Array
(
[id] => 1
[name] => earth
[color] => blue
)
PDO::FETCH_NUM
При использовании этого режима результат представляется в виде массива, индексированного номерами столбцов (начиная с 0):
Array
(
[0] => 1
[1] => earth
[2] => blue
)
PDO::FETCH_COLUMN
Этот вариант полезен, если нужно получить перечень значений одного поля в виде одномерного массива, нумерация которого начинается с 0. Например:
$stmt = $pdo->query("SELECT name FROM planets");
В результате получим:
Array
(
[0] => earth
[1] => mars
[2] => jupiter
)
PDO::FETCH_KEY_PAIR
Используем этот вариант, если нужно получить перечень значений двух полей в виде ассоциативного массива. Ключи массива — это данные первого столбца выборки, значения массива — данные второго столбца. Например:
$stmt = $pdo->query("SELECT name, color FROM planets");
$result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
В результате получим:
Array
(
[earth] => blue
[mars] => red
[jupiter] => strange
)
PDO::FETCH_OBJECT
При использовании PDO::FETCH_OBJECT для каждой извлеченной строки создаётся анонимный объект. Его общедоступные (public) свойства — имена столбцов выборки, а результаты запроса используются в качестве их значений:
$stmt = $pdo->query("SELECT name, color FROM planets");
$results = $stmt->fetch(PDO::FETCH_OBJ);
В результате получим:
stdClass Object
(
[name] => earth
[color] => blue
)
PDO::FETCH_CLASS
В этом случае, как и в предыдущем, значения столбцов становятся свойствами объекта. Однако требуется указать существующий класс, который будет использоваться для создания объекта. Рассмотрим это на примере. Для начала создадим класс:
class Planet
{
private $name;
private $color;
public function setName($planet_name)
{
$this->name = $planet_name;
}
public function setColor($planet_color)
{
$this->color = $planet_color;
}
public function getName()
{
return $this->name;
}
public function getColor()
{
return $this->color;
}
}
Обратите внимание, что у класса Planet закрытые (private) свойства и нет конструктора. Теперь выполним запрос.
Если используется метод fetch с PDO::FETCH_CLASS, перед отправкой запроса на получение данных нужно применить метод setFetchMode:
$stmt = $pdo->query("SELECT name, color FROM planets");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'Planet');
Первый параметр, который передаем методу setFetchMode, — константа PDO::FETCH_CLASS. Второй параметр — имя класса, который будет использоваться при создании объекта. Теперь выполним:
$planet = $stmt->fetch();
var_dump($planet);
В результате получим объект Planet:
Planet Object
(
[name:Planet:private] => earth
[color:Planet:private] => blue
)
Значения, полученные в результате запроса, назначены соответствующим свойствам объекта, даже закрытым.
Определение свойств после выполнения конструктора
В классе Planet нет явного конструктора, поэтому проблем при назначении свойств не будет. При наличии у класса конструктора, в котором свойство было назначено или изменено, они будут перезаписаны.
При использовании константы FETCH_PROPS_LATE значения свойств будут присваиваться после выполнения конструктора:
class Planet
{
private $name;
private $color;
public function __construct($name = moon, $color = grey)
{
$this->name = $name;
$this->color = $color;
}
public function setName($planet_name)
{
$this->name = $planet_name;
}
public function setColor($planet_color)
{
$this->color = $planet_color;
}
public function getName()
{
return $this->name;
}
public function getColor()
{
return $this->color;
}
}
Мы изменили класс Planet, добавив конструктор, который принимает на входе два аргумента: name (имя) и color (цвет). Значения этих полей по умолчанию: moon (луна) и gray (серый) соответственно.
Если не использовать FETCH_PROPS_LATE, при создании объекта свойства будут перезаписаны значениями по умолчанию. Проверим это. Сначала выполним запрос:
$stmt = $pdo->query("SELECT name, color FROM solar_system WHERE name = 'earth'");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'Planet');
$planet = $stmt->fetch();
var_dump($planet);
В результате получим:
object(Planet)#2 (2) {
["name":"Planet":private]=>
string(4) "moon"
["color":"Planet":private]=>
string(4) "gray"
}
Как и ожидалось, извлеченные из базы данных значения перезаписаны. Теперь рассмотрим решение задачи с помощью FETCH_PROPS_LATE (запрос аналогичный):
$stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Planet');
$planet = $stmt->fetch();
var_dump($planet);
В результате получим то, что нужно:
object(Planet)#4 (2) {
["name":"Planet":private]=>
string(5) "earth"
["color":"Planet":private]=>
string(4) "blue"
}
Если у конструктора класса нет значений по умолчанию, а они нужны, параметры конструктора задаются при вызове метода setFetchMode третьим аргументом в виде массива. Например:
class Planet
{
private $name;
private $color;
public function __construct($name, $color)
{
$this->name = $name;
$this->color = $color;
}
[...]
}
Аргументы конструктора обязательны, поэтому выполним:
$stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Planet', ['moon', 'gray']);
Входящие параметры выступают также в роли значений по умолчанию, которые нужны для инициализации. В дальнейшем они будут перезаписаны значениями из базы данных.
Получение нескольких объектов
Множественные результаты извлекаются в виде объектов с помощью метода fetch внутри цикла while:
while ($planet = $stmt->fetch()) {
// обработка результатов
}
Или путём выборки всех результатов сразу. Во втором случае используется метод fetchAll, причём режим указывается в момент вызова:
$stmt->fetchAll(PDO::FETCH_CLASS|PDO_FETCH_PROPS_LATE, 'Planet', ['moon', 'gray']);
PDO::FETCH_INTO
При выборе этого варианта выборки PDO не создаёт новый объект, а обновляет свойства существующего. Однако это возможно только для общедоступных (public) свойств или при использовании в объекте «магического» метода __set.
Подготовленные и прямые запросы
В PDO два способа выполнения запросов:
- прямой, который состоит из одного шага;
- подготовленный, который состоит из двух шагов.
Прямые запросы
Существует два метода выполнения прямых запросов:
queryиспользуется для операторов, которые не вносят изменения, напримерSELECT. Возвращает объектPDOStatemnt, из которого с помощью методовfetchилиfetchAllизвлекаются результаты запроса;execиспользуется для операторов вродеINSERT,DELETEилиUPDATE. Возвращает число обработанных запросом строк.
Прямые операторы используются только в том случае, если в запросе отсутствуют переменные и есть уверенность, что запрос безопасен и правильно экранирован.
Подготовленные запросы
PDO поддерживает подготовленные запросы (prepared statements), которые полезны для защиты приложения от SQL-инъекций: метод prepare выполняет необходимые экранирования.
Рассмотрим пример. Требуется вставить свойства объекта Planet в таблицу Planets. Сначала подготовим запрос:
$stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(?, ?)");
Используем метод prepare, который принимает как аргумент SQL-запрос с псевдопеременными (placeholders). Псевдопеременные могут быть двух типов: неименнованые и именованные.
Неименованные псевдопеременные
Неименованные псевдопеременные (positional placeholders) отмечаются символом ?. Запрос в результате получается компактным, но требуется предоставить значения для подстановки, размещенные в том же порядке. Они передаются в виде массива через метод execute:
$stmt->execute([$planet->name, $planet->color]);
Именованные псевдопеременные
При использовании именованных псевдопеременных (named placeholders) порядок передачи значений для подстановки не важен, но код в этом случае становится не таким компактным. В метод execute данные передаются в виде ассоциативного массива, в котором каждый ключ соответствует имени псевдопеременной, а значение массива — значению, которое требуется подставить в запрос. Переделаем предыдущий пример:
$stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(:name, :color)");
$stmt->execute(['name' => $planet->name, 'color' => $planet->color]);
Методы prepare и execute используются как при выполнении запросов на изменение, так и при выборке.
А информацию о количестве обработанных строк при необходимости предоставит метод rowCount.
Управление поведением PDO при ошибках
Параметр выбора режима ошибок PDO::ATTR_ERRMODE используется для определения поведения PDO в случае ошибок. Доступно три варианта: PDO::ERRMODE_SILENT, PDO::ERRMODE_EXCEPTION и PDO::ERRMODE_WARNING.
PDO::ERRMODE_SILENT
Вариант по умолчанию. PDO просто запишет информацию об ошибке, которую помогут получить методы errorCode и errorInfo.
PDO::ERRMODE_EXCEPTION
Это предпочтительный вариант, при котором в дополнение к информации об ошибке PDO выбрасывает исключение (PDOException). Исключение прерывает выполнение скрипта, что полезно при использовании транзакций PDO. Пример приведён ниже при описании транзакций.
PDO::ERRMODE_WARNING
В этом случае PDO также записывает информацию об ошибке. Поток выполнения скрипта не прерывается, но выдаются предупреждения.
Методы bindValue и bindParam
Для подстановки значений в запросе можно также использовать методы bindValue и bindParam. Первый связывает значение переменной с псевдопеременной, которая использована при подготовке запроса:
$stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(:name, :color)");
$stmt->bindValue('name', $planet->name, PDO::PARAM_STR);
Связали значение переменной $planet->name с псевдопеременной :name. Обратите внимание, что при использовании методов bindValue и bindParam как третий аргумент указывается тип переменной, используя соответствующие константы PDO. В примере — PDO::PARAM_STR.
Метод bindParam привязывает переменную к псевдопеременной. В этом случае переменная связана с псевдопеременной ссылкой, а значение будет подставлено в запрос только после вызова метода execute. Рассмотрим на примере:
$stmt->bindParam('name', $planet->name, PDO::PARAM_STR);
Транзакции в PDO
Транзакции позволяют сохранить на некоторое время и организовать выполнение нескольких запросов «пакетом». Запросы, включённые в транзакцию, применяются только в том случае, если при выполнении отсутствуют ошибки. Транзакции поддерживаются не всеми СУБД и работают не со всеми SQL-конструкциями, так как некоторые из них вызывают неявное выполнение. Список таких конструкций можно найти на сайте MariaDB.
Представим необычный пример. Пользователю требуется выбрать список планет, причём каждый раз при выполнении запроса текущие данные удаляются из базы, а потом вставляются новые. Если после удаления произойдёт ошибка, то следующий пользователь получит пустой список. Чтобы этого избежать, используем транзакции:
$pdo->beginTransaction();
try {
$stmt1 = $pdo->exec("DELETE FROM planets");
$stmt2 = $pdo->prepare("INSERT INTO planets(name, color) VALUES (?, ?)");
foreach ($planets as $planet) {
$stmt2->execute([$planet->getName(), $planet->getColor()]);
}
$pdo->commit();
} catch (PDOException $e) {
$pdo->rollBack();
}
Метод beginTransaction отключает автоматическое выполнение запросов, а внутри конструкции try-catch запросы выполняются в нужном порядке. Если не возникнет исключений PDOException, запросы выполнятся с помощью метода commit. В противном случае откатятся с помощью метода rollback, а автоматическое выполнение запросов восстановится.
Таким образом появилась согласованность выполнения запросов. Очевидно, что для этого параметру PDO::ATTR_ERRMODE необходимо установить значение PDO::ERRMODE_EXCEPTION.
Заключение
Теперь, когда работа с PDO описана, отметим его основные преимущества:
- с PDO легко перенести приложение на другие СУБД;
- поддерживаются все популярные СУБД;
- встроенная система управления ошибками;
- разнообразные варианты представления результатов выборки;
- поддерживаются подготовленные запросы, которые сокращают код и делают его устойчивым к SQL-инъекциям;
- поддерживаются транзакции, которые помогают сохранить целостность данных и согласованность запросов при параллельной работе пользователей.
Перевод статьи «How to configure and use PDO for database access on Linux»
В этой статье разговор пойдет о PHP Data Objects (PDO) — расширение для PHP, предоставляющее разработчику универсальный интерфейс для доступа к различным базам данных.
В чем преимущество PDO? Этот вопрос можно раскрыть тремя пунктами:
- Универсальный интерфейс для работы с различными базами данных. Разработчик может легко перевести свое веб-приложение на другую СУБД, поменяв при этом всего пару строк кода.
- Высокая скорость работы.
- Подготовленные выражения, о которых мы поговорим чуть позже.
На данный момент расширение PDO может поддерживать СУБД для которой существует PDO-драйвер:
- PDO_CUBRID (CUBRID)
- PDO_DBLIB (FreeTDS, Microsoft SQL Server, Sybase)
- PDO_FIREBIRD (Firebird, Interbase 6)
- PDO_IBM (IBM DB2)
- PDO_INFORMIX (IBM Informix Dynamic Server)
- PDO_MYSQL (MySQL 3.x/4.x/5.x)
- PDO_OCI (Oracle Call Interface)
- PDO_ODBC (ODBC v3 (IBM DB2, unixODBC and win32 ODBC))
- PDO_PGSQL (PostgreSQL)
- PDO_SQLITE (SQLite 3 and SQLite 2)
- PDO_SQLSRV (Microsoft SQL Serve )
- PDO_4D (4D)
Подключение к базе данных
В зависимости от выбранной СУБД, способ подключения может незначительно отличаться. Подключение к популярным СУБД:
// MуSQL $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); // PostgreSQL $DBH = new PDO("pgsql:host=$host;dbname=$dbname", $user, $pass); //MS SQL $DBH = new PDO("mssql:host=$host;dbname=$dbname", $user, $pass); // SQLite $DBH = new PDO("sqlite:my/database/path/database.db");
Обработка ошибок и исключения
Обязательно заключайте подключение к базе данных в блок try/catch:
try { $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); } catch(PDOException $e) { echo "Нет соединения с базой данных"; }
Иначе, в случае ошибки, в браузер будет выкинут Fatal error, в котором будут раскрыты все подробности вашего соединения, с логином и паролем.
PDO умеет выбрасывать исключения при ошибках, поэтому все операции с базой, по хорошему, должны находиться в блоке try/catch.
PDO имеет три режима обработки исключения:
- PDO::ERRMODE_SILENT — режим по умолчанию, ошибки генерируются по такому же принципу, как в расширениях mysql или mysqli. После возникновения ошибки скрипт продолжит работу.
- PDO::ERRMODE_WARNING — режим вызовет стандартное сообщение E_WARNING и позволит скрипту продолжить работу.
- PDO::ERRMODE_EXCEPTION — режим выбрасывает исключение, что позволяет обрабатывать ошибки и скрывать важную информацию от посторонних глаз.
Чтобы установить необходимый уровень контроля ошибок необходимо вызвать метод $this->setAttribute после подключения к базе данных.
try { $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e) { echo "Нет соединения с базой данных"; }
Подготовленные выражения или Prepared Statements
Подготовленные выражения — это заранее скомпилированное SQL-выражение, которое может быть многократно выполнено путем отправки серверу лишь различных наборов данных. Дополнительным преимуществом является невозможность провести SQL-инъекцию.
За подготовленные выражения отвечает метод $DBH->prepare. Им и рекомендуется всегда пользоваться.
Сразу хочу отметить, что выполнить запрос в PDO можно тремя методами:
- $DBH->exec — используется для запросов, которые не возвращают никаких данных. Метод возвращает количество затронутых им записей, или FALSE в случае ошибки.
$count_row = $DBH->exec("DELETE FROM users");
- $DBH->query – используется выполнения не защищенных запросов, и возвращает результат или FALSE в случаи ошибки. Например, им можно выполнять простые запросы.
$DBH->query("SET NAMES 'cp1251'"); $DBH->query("SELECT * FROM users"); $DBH->query("DELETE FROM users");
- $DBH->prepare + $STH->execute — используется для подготовки выражений и выполнения запроса.
// безымянные placeholders $STH = $DBH->prepare("INSERT INTO users (name, phone, city) values (?, ?, ?)");
// именные placeholders $STH = $DBH->prepare("INSERT INTO users (name, phone, city) values (:name, :phone, :city)");
После подготовки, запрос выполняется методом $STH->execute($data).
// безымянные placeholders $data = array(); $data[] = 'Alersander'; $data[] = '+7 000 123 45 67'; $data[] = 'St. Petersburg'; $STH = $DBH->prepare("INSERT INTO users (name, phone, city) values (?, ?, ?)"); $STH->execute($data);
// именные placeholders $data = array(); $data['name'] = 'Alersander'; $data['phone'] = '+7 000 123 45 67'; $data['city'] = 'St. Petersburg'; $STH = $DBH->prepare("INSERT INTO users (name, phone, city) values (:name, :phone, :city)"); $STH->execute($data);
Выборка данных
Для выборки с произвольными параметрами тоже будем использовать метод $DBH->prepare. Данные выборки можно получить с помощью методов:
- $STH->fetch — выбирает следующую строку и возвращает в необходимом виде.
- $STH->fetchAll — возвращает массив всех выбранных строк.
- $STH->fetchObject — выбирает следующую строку и возвращает ее как объект.
Я буду рассматривать только первый метод, т.к. он универсальный и предоставляет разработчику, всё, что ему необходимо.
Методу $STH->fetch можно указать, в каком виде нам нужно предоставить результат:
- PDO::FETCH_BOTH (по умолчанию) — возвращает числовой и ассоциативный массив;
- PDO::FETCH_ASSOC — возвращает массив ассоциативный с названиями столбцов;
- PDO::FETCH_NUM — возвращает массив числовыми ключами в виде порядковых номеров столбцов;
- PDO::FETCH_OBJ — возвращает анонимный объект со свойствами, соответствующими именам столбцов;
- PDO::FETCH_BOUND — присваивает значения столбцов соответствующим переменным, заданным с помощью метода $STH->bindColumn();
- PDO::FETCH_CLASS — присваивает значения столбцов соответствующим свойствам указанного класса. Если для какого-то столбца свойства нет, оно будет создано;
- PDO::FETCH_INTO — обновляет существующий экземпляр указанного класса;
- PDO::FETCH_LAZY — объединяет в себе PDO::FETCH_BOTH и PDO::FETCH_OBJ.
$STH = $DBH->prepare("SELECT name, phone, city FROM users"); $STH->execute(); while($res = $STH->fetch(PDO::FETCH_ASSOC)) { echo $res['name']; } $STH = $DBH->prepare("SELECT name, phone, city FROM users"); $STH->execute(); while($res = $STH->fetch(PDO::FETCH_OBJ)) { echo $res->name; }
Закрытие соединения и освобождение буфера запроса
В PDO нет специальных методов для этих целей. Закрыть соединение с базой данных можно путем переопределения переменных:
$DBH = null; $STH = null;
Полезные методы
- $DBH->lastInsertId() — возвращает id последней вставленной записи.
- $DBH->query(string) — экранирует специальные символы в строковых данных таким образом, что их становится безопасно использовать в запросах.
- $STH->rowCount() — возвращает количество затронутых записей последним запросом.
Обработка ошибок PDO
Лично мне не нравится, что если я не заключаю все запросы в блок try/catch, то PDO выкидывает Fatal error со всеми интимными подробностями моего запроса. В промышленном приложении заключать каждый запрос в блок try/catch, это идиотизм!
Поэтому мы поступим следующим образом — немного расширим классы PDO и PDOStatement:
class DB extends PDO { public $error = false; // выводить сообщения об ошибках на экран? (true/false) public function __construct($dsn, $username='', $password='', $driver_options=array()) { try { parent::__construct($dsn, $username, $password, $driver_options); $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DBStatement', array($this))); $this->query("SET NAMES 'cp1251'"); } catch(PDOException $e) { echo "Произошла ошибка в работе с базой данных..."; exit(); } } public function prepare($sql, $driver_options=array()) { try { return parent::prepare($sql, $driver_options); } catch(PDOException $e) { $this->error($e->getMessage()); } } public function query($sql) { try { return parent::query($sql); } catch(PDOException $e) { $this->error($e->getMessage()); } } public function exec($sql) { try { return parent::exec($sql); } catch(PDOException $e) { $this->error($e->getMessage()); } } public function error($msg) { if($this->error) { echo $msg; } else { echo "Произошла ошибка в работе с базой данных..."; } exit(); } }
class DBStatement extends PDOStatement { protected $DBH; protected function __construct($DBH) { $this->DBH = $DBH; } public function execute($data=array()) { try { return parent::execute($data); } catch(PDOException $e) { $this->DBH->error($e->getMessage()); } } }
Как видите, я реализую свои два класса DB и DBStatement, наследуя классы PDO и PDOStatement. Классы реализуют все необходимые мне для работы над ошибками методы, которые обрабатываются блоком try/catch.
$DBH = new DB("mysql:host=$host;dbname=$dbname", $user, $pass); $DBH->error = true; // Для отладки выводим сообщения об ошибках на экран. $STH = $DBH->prepare("SELEC * FROM users"); $STH->execute($data);
Как видите, я совершил опечатку в операторе и могу получить всю необходимую информацию об этой ошибке.
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELEC * FROM users' at line 1
Расширение функционала
Также бывает полезно расширить функционал PDO. Например, часто требуется получить количество записей в таблице.
Стандартными методами это можно делать следующим образом:
$data = array(); $data['user_id'] = 1; $STH = $DBH->prepare("SELECT COUNT(*) as count FROM users WHERE user_id=:user_id"); $STH->execute($data); echo $STH->fetch(PDO::FETCH_OBJ)->count;
Не слишком удобно. Поэтому реализуем в нашем классе методом count:
class DB extends PDO { // .................. public function count($sql, $data) { $res = $this->prepare($sql); $res->execute($data); return $res->fetch(PDO::FETCH_OBJ); } // .................. }
Получаем количество записей:
$DBH = new DB("mysql:host=$host;dbname=$dbname", $user, $pass); echo $DBH->count("SELECT COUNT(*) as count FROM users WHERE user_id=:user_id", array('user_id'=>'1'))->count;
Свои методы это конечно хорошо, но мне бы ещё хотелось писать все одной строкой. К сожалению, стандартными возможностями я не могу этого сделать:
$res = $DBH->prepare("SELECT * FROM users WHERE id_user=:id_user") ->execute(array('id_user'=>'1')) ->fetch(PDO::FETCH_ASSOC); echo $res['name']; // или так echo $DBH->prepare("SELECT *FROM users WHERE id_user=:id_user") ->execute(array('id_user'=>'1')) ->fetch(PDO::FETCH_OB) ->name;
Метод execute возвращает бесполезное логическое значение об успехе операции, а не объект DBStatement.
Допиливаем свой метод execute:
public function execute($data=array()) { try { parent::execute($data); return $this; } catch(PDOException $e) { $this->DBH->error($e->getMessage()); } }
Проверяем результат:
echo $DBH->prepare("SELECT COUNT(*) as count FROM users WHERE city=:city") ->execute(array('city'=>'St. Petersburg')) ->fetch(PDO::FETCH_OBJ) ->count;
Источник
Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Сегодня мы поговорим про использование транзакций в PDO на PHP. Если вы плохо себе представляете, что такое транзакции, то для лучшего понимания советую сначала прочитать статью «Введение в транзакции в MySQL«.
Чтобы начать транзакцию, необходимо выполнить метод «beginTransaction()» у объекта класса «PDO». Рассмотрим пример на php:
$dsn = 'mysql:dbname=1;host=localhost';
$user = 'root';
$password = '';
$driver = array(PDO :: MYSQL_ATTR_INIT_COMMAND => 'SET NAMES `utf8`');
try {
$db = new PDO($dsn, $user, $password, $driver); //создаем новый объект класса PDO для взаимодействия с БД
} catch (PDOException $e) {
echo 'Подключение не удалось: '. $e->getCode() .'|'. $e->getMessage());
exit();
}
$db->beginTransaction(); //Начинаем транзакцию
$db->exec("INSERT INTO user VALUES (1, 'Коля')");
$db->exec("INSERT INTO user VALUES (2, 'Алексей')");
$db->exec("INSERT INTO user VALUES (1, 'Иван')");
...
//далее commit() или rollBack()
Чтобы зафиксировать изменения в транзакции, у объекта PDO нужно выполнить метод commit():
$db->commit();
Чтобы отменить изменения (откатить транзакцию), у объекта $db PDO необходимо вызвать метод rollBack():
$db->rollBack();
Обратите внимание, если начать транзакцию и ее не завершить (то есть в рамках скрипта не выполнить ни commit() ни rollback()), то при завершении работы скрипта транзакция откатится автоматически, если не установлено постоянного соединения с БД (не установлен атрибут PDO::ATTR_PERSISTENT => true). Тоже самое произойдет при уничтожении PDO объекта ($db=null) в коде скрипта, в этом случае PDO завершит текущее соединение с БД. Откат транзакции при завершении соединения с БД делает PDO драйвер, это очень удобно при аварийном завершении скриптов.
Транзакции доступны только для таблиц с типом InnoDB. Для MyISAM таблиц транзакции недоступны.
По умолчанию в MySQL включен autocommit. Это означает подтверждение (фиксацию) каждого запроса к БД, это означает, что каждый запрос к базе данных в MySQL по умолчанию является транзакцией. Поэтому вставка данных в таблицы типа InnoDB идет медленнее, чем в таблицы типа MyISAM. При импорте данных и вставке больших объемов информации в таблицы InnoDB следует отключать autocommit и фиксировать изменения, т. е. делать commit не после каждой вставки, а после нескольких вставок (коммитить только после совершения группы запросов).
Обработка ошибок PDO в PHP и откат транзакций при ошибках
По умолчанию в PDO установлен «тихий» режим обработки ошибок (silent mode). Это означает, что при возникновении ошибки в PDO, исключение выброшено не будет и работа скрипта продолжится. Ошибки не будут ловиться с помощью try catch блоков, код ошибки и описание будет возможно получить только с помощью специальных методов у объекта PDO или PDOStatement: errorCode() и errorInfo(). Для того, чтобы ошибки PDO можно было «ловить» в try..catch, нужно изменить режим обработки ошибок с PDO::ERRMODE_SILENT на PDO::ERRMODE_EXCEPTION. Внимание: после установки этого режима желательно обрабатывать исключения при каждом запросе к БД, так как при возникновении ошибки остановится работа скрипта и произойдет остановка всего web-приложения. Если вы устанавливайте этот режим, обязательно используйте try..catch блоки в каждом запросе, чтобы ловить ошибки .
Рассмотрим, как изменится логика приложения после включения режима обработки ошибок PDO::ERRMODE_EXCEPTION:
$dsn = 'mysql:dbname=1;host=localhost';$user = 'root';$password = '';
$driver = array(PDO :: MYSQL_ATTR_INIT_COMMAND => 'SET NAMES `utf8`');
try {
$db = new PDO($dsn, $user, $password, $driver);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //Устанавливаем режим обработки ошибок ERRMODE_EXCEPTION
} catch (PDOException $e) {
echo 'Подключение не удалось: '. $e->getCode() .'|'. $e->getMessage();
exit();
}
try {
$db->beginTransaction(); //Начинаем транзакцию
$db->exec("INSERT INTO user VALUES (1, 'Коля')");
$db->exec("INSERT INTO user VALUES (2, 'Алексей')");
$db->exec("INSERT INTO user VALUES (1, 'Иван')");
catch (PDOException $e) { //Ловим ошибку
$db->rollBack();
echo 'PDOException: '.$e->getCode() .'|'. $e->getMessage());
exit();
}
$db->commit(); //Если все запросы прошли успешно - коммитим
Это простой пример обработки ошибок при использовании транзакций. В реальных приложениях нужно обязательно смотреть код ошибки. Если, например, это отключение от MySQL сервера, то совсем необязательно завершать работу скрипта после отката транзакции. В этом случае можно попытаться переподключиться к SQL серверу через какой то промежуток времени и пробовать заново выполнить текущий запрос или транзакцию. Если это, например, ошибка несовпадения типа данных — то в этом случае конечно нет смысла повторять запрос, можно откатывать транзакцию и завершить работу скрипта. При возникновении определенных ошибок вообще можно не откатывать транзакцию. Вообщем надо смотреть код SQL ошибки — и уже посмотрев решать как дальше поступать.
Рассмотрим пример:
function connect_db() {
$dsn = 'mysql:dbname=1;host=localhost';
$user = 'root';
$password = '';
$driver = array(PDO :: MYSQL_ATTR_INIT_COMMAND => 'SET NAMES `utf8`');
try {
$db = new PDO($dsn, $user, $password, $driver); //создаем новый объект класса PDO для взаимодействия с БД
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //Устанавливаем режим обработки ошибок ERRMODE_EXCEPTION
} catch (PDOException $e) {
echo 'Подключение не удалось: '. $e->getCode() .'|'. $e->getMessage());
return false;
}
return $db;
}
function doQuery($db, $sql, $count_db = 0) {
if($count_db>5) {
echo "Кол-во попыток подключения превысило допустимый лимит";
return false;
}
try {
if($db->inTransaction()) {
echo "Транзакция уже начата";
return false;
}
$db->beginTransaction();//Начинаем транзакцию
$db->exec($sql);
} catch (PDOException $e) {
if($db->inTransaction())
$db->rollBack();
if($e->errorInfo[1] >= 2000&&$db=connect_db()) { //если код ошибки > 2000 (это потеря соединения с БД и пр.) то пробуем переподключится и выполнить запрос заново
return doQuery($db, $sql, $count_db++);
} else {
echo 'PDOException: '.$e->getCode() .'|'. $e->getMessage();
return false;
}
}
if($db->inTransaction())
return $db->commit();
}
Обратите внимание на метод:
$db->inTransaction();
Он проверяет, начата ли транзакция или нет. Это очень важно, так как если вызвать метод beginTransaction() в том случае, если транзакция уже начата, или наоборот вызвать метод rollBack() или commit() когда транзакция не еще начата, то в любом из этих случаев вы получите ФАТАЛЬНУЮ ошибку. Да, поэтому всегда проверяйте начата ли транзакция, прежде чем ее завершить, в противном случае вы просто словите ошибку и ваше приложение аварийно завершится.

Перевод статьи Why you Should be using PHP’s PDO for Database Access.
Множество PHP-разработчиков привыкли использовать для работы с базами данных расширения mysql и mysqli. Но с версии 5.1 в PHP существует более удобный способ — PHP Data Objects. Этот класс, сокращенно именуемый PDO, предоставляет методы для работы с объектами и prepared statements, которые заметно повысят вашу продуктивность!
Введение в PDO
«PDO – PHP Data Objects – это прослойка, которая предлагает универсальный способ работы с несколькими базами данных.»
Заботу об особенностях синтаксиса различных СУБД она оставляет разработчику, но делает процесс переключения между платформами гораздо менее болезненным. Нередко для этого требуется лишь изменить строку подключения к базе данных.

Эта статья написана для людей, которые пользуются mysql и mysqli, чтобы помочь им в переходе на более мощный и гибкий PDO.
Поддержка СУБД
Это расширение может поддерживать любую систему управления базами данных, для которой существует PDO-драйвер. На момент написания статьи доступны следующие драйвера:
- PDO_CUBRID ( CUBRID )
- PDO_DBLIB ( FreeTDS / Microsoft SQL Server / Sybase )
- PDO_FIREBIRD ( Firebird/Interbase 6 )
- PDO_IBM ( IBM DB2 )
- PDO_INFORMIX ( IBM Informix Dynamic Server )
- PDO_MYSQL ( MySQL 3.x/4.x/5.x )
- PDO_OCI ( Oracle Call Interface )
- PDO_ODBC ( ODBC v3 (IBM DB2, unixODBC and win32 ODBC) )
- PDO_PGSQL ( PostgreSQL )
- PDO_SQLITE ( SQLite 3 and SQLite 2 )
- PDO_SQLSRV ( Microsoft SQL Server )
- PDO_4D ( 4D )
Впрочем, не все из них есть на вашем сервере. Увидеть список доступных драйверов можно так:
print_r(PDO::getAvailableDrivers());
Подключение
Способы подключения к разным СУБД могут незначительно отличаться. Ниже приведены примеры подключения к наиболее популярным из них. Можно заметить, что первые три имеют идентичный синтаксис, в отличие от SQLite.
try {
# MS SQL Server и Sybase через PDO_DBLIB
$DBH = new PDO("mssql:host=$host;dbname=$dbname", $user, $pass);
$DBH = new PDO("sybase:host=$host;dbname=$dbname", $user, $pass);
# MySQL через PDO_MYSQL
$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
# SQLite
$DBH = new PDO("sqlite:my/database/path/database.db");
}
catch(PDOException $e) {
echo $e->getMessage();
}
Пожалуйста, обратите внимание на блок try/catch – всегда стоит оборачивать в него все свои PDO-операции и использовать механизм исключений (об этом чуть дальше).
$DBH расшифровывается как «database handle» и будет использоваться на протяжении всей статьи.
Закрыть любое подключение можно путем переопределения его переменной в null.
# закрывает подключение
$DBH = null;
Больше информации по теме отличительных опций разных СУБД и методах подключения к ним можно найти на php.net.
Исключения и PDO
PDO умеет выбрасывать исключения при ошибках, поэтому все должно находиться в блоке try/catch. Сразу после создания подключения, PDO можно перевести в любой из трех режимов ошибок:
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
Но стоит заметить, что ошибка при попытке соединения будет всегда вызывать исключение.
PDO::ERRMODE_SILENT
Это режим по умолчанию. Примерно то же самое вы, скорее всего, используете для отлавливания ошибок в расширениях mysql и mysqli. Следующие два режима больше подходят для DRY программирования.
PDO::ERRMODE_WARNING
Этот режим вызовет стандартный Warning и позволит скрипту продолжить выполнение. Удобен при отладке.
PDO::ERRMODE_EXCEPTION
В большинстве ситуаций этот тип контроля выполнения скрипта предпочтителен. Он выбрасывает исключение, что позволяет вам ловко обрабатывать ошибки и скрывать щепетильную информацию. Как, например, тут:
# подключаемся к базе данных
try {
$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
# Черт! Набрал DELECT вместо SELECT!
$DBH->prepare('DELECT name FROM people')->execute();
}
catch(PDOException $e) {
echo "Хьюстон, у нас проблемы.";
file_put_contents('PDOErrors.txt', $e->getMessage(), FILE_APPEND);
}
В SQL-выражении есть синтаксическая ошибка, которая вызовет исключение. Мы можем записать детали ошибки в лог-файл и человеческим языком намекнуть пользователю, что что-то случилось.
Insert и Update
Вставка новых и обновление существующих данных являются одними из наиболее частых операций с БД. В случае с PDO этот процесс обычно состоит из двух шагов. (В следующей секции все относится как к UPDATE, так и INSERT)

Тривиальный пример вставки новых данных:
# STH означает "Statement Handle"
$STH = $DBH->prepare("INSERT INTO folks ( first_name ) values ( 'Cathy' )");
$STH->execute();
Вообще-то можно сделать то же самое одним методом exec(), но двухшаговый способ дает все преимущества prepared statements. Они помогают в защите от SQL-инъекций, поэтому имеет смысл их использовать даже при однократном запросе.
Prepared Statements
Использование prepared statements укрепляет защиту от SQL-инъекций.
Prepared statement — это заранее скомпилированное SQL-выражение, которое может быть многократно выполнено путем отправки серверу лишь различных наборов данных. Дополнительным преимуществом является невозможность провести SQL-инъекцию через данные, используемые в placeholder’ах.
Ниже находятся три примера 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.
Безымянные placeholder’ы
# назначаем переменные каждому placeholder, с индексами от 1 до 3
$STH->bindParam(1, $name);
$STH->bindParam(2, $addr);
$STH->bindParam(3, $city);
# вставляем одну строку
$name = "Daniel"
$addr = "1 Wicked Way";
$city = "Arlington Heights";
$STH->execute();
# вставляем еще одну строку, уже с другими данными
$name = "Steve"
$addr = "5 Circle Drive";
$city = "Schaumburg";
$STH->execute();
Здесь два шага. На первом мы назначаем всем placeholder’ам переменные (строки 2-4). Затем назначаем этим переменным значения и выполняем запрос. Чтобы послать новый набор данных, просто измените значения переменных и выполните запрос еще раз.
Если в вашем SQL-выражении много параметров, то назначать каждому по переменной весьма неудобно. В таких случаях можно хранить данные в массиве и передавать его:
# набор данных, которые мы будем вставлять
$data = array('Cathy', '9 Dark and Twisty Road', 'Cardiff');
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)");
$STH->execute($data);
$data[0] вставится на место первого placeholder’а, $data[1] — на место второго, и т.д. Но будьте внимательны: если ваши индексы сбиты, это работать не будет.
Именные placeholder’ы
# первым аргументом является имя placeholder’а
# его принято начинать с двоеточия
# хотя работает и без них
$STH->bindParam(':name', $name);
Здесь тоже можно передавать массив, но он должен быть ассоциативным. В роли ключей должны выступать, как можно догадаться, имена placeholder’ов.
# данные, которые мы вставляем
$data = array( 'name' => 'Cathy', 'addr' => '9 Dark and Twisty', 'city' => 'Cardiff' );
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");
$STH->execute($data);
Одним из удобств использования именных placeholder’ов является возможность вставки объектов напрямую в базу данных, если названия свойств совпадают с именами параметров. Вставку данных, к примеру, вы можете выполнить так:
# класс для простенького объекта
class person {
public $name;
public $addr;
public $city;
function __construct($n,$a,$c) {
$this->name = $n;
$this->addr = $a;
$this->city = $c;
}
# так далее...
}
$cathy = new person('Cathy','9 Dark and Twisty','Cardiff');
# а тут самое интересное
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");
$STH->execute((array)$cathy);
Преобразование объекта в массив при execute() приводит к тому, что свойства считаются ключами массива.
Выборка данных

Данные можно получить с помощью метода ->fetch(). Перед его вызовом желательно явно указать, в каком виде они вам требуются. Есть несколько вариантов:
- PDO::FETCH_ASSOC: возвращает массив с названиями столбцов в виде ключей
- PDO::FETCH_BOTH (по умолчанию): возвращает массив с индексами как в виде названий стобцов, так и их порядковых номеров
- PDO::FETCH_BOUND: присваивает значения столбцов соответствующим переменным, заданным с помощью метода ->bindColumn()
- PDO::FETCH_CLASS: присваивает значения столбцов соответствующим свойствам указанного класса. Если для какого-то столбца свойства нет, оно будет создано
- PDO::FETCH_INTO: обновляет существующий экземпляр указанного класса
- PDO::FETCH_LAZY: объединяет в себе PDO::FETCH_BOTH и PDO::FETCH_OBJ
- PDO::FETCH_NUM: возвращает массив с ключами в виде порядковых номеров столбцов
- PDO::FETCH_OBJ: возвращает анонимный объект со свойствами, соответствующими именам столбцов
На практике вам обычно хватит трех: FETCH_ASSOC, FETCH_CLASS, и FETCH_OBJ. Чтобы задать формат данных, используется следующий синтаксис:
$STH->setFetchMode(PDO::FETCH_ASSOC);
Также можно задать его напрямую при вызове метода ->fetch().
FETCH_ASSOC
При этом формате создается ассоциативный массив с названиями столбцов в виде индексов. Он должен быть знаком тем, кто использует расширения mysql/mysqli.
# поскольку это обычный запрос без placeholder’ов,
# можно сразу использовать метод query()
$STH = $DBH->query('SELECT name, addr, city from folks');
# устанавливаем режим выборки
$STH->setFetchMode(PDO::FETCH_ASSOC);
while($row = $STH->fetch()) {
echo $row['name'] . "n";
echo $row['addr'] . "n";
echo $row['city'] . "n";
}
Цикл while() переберет весь результат запроса.
FETCH_OBJ
Данный тип получения данных создает экземпляр класса std для каждой строки.
# создаем запрос
$STH = $DBH->query('SELECT name, addr, city from folks');
# выбираем режим выборки
$STH->setFetchMode(PDO::FETCH_OBJ);
# выводим результат
while($row = $STH->fetch()) {
echo $row->name . "n";
echo $row->addr . "n";
echo $row->city . "n";
}
FETCH_CLASS
При использовании fetch_class данные заносятся в экземпляры указанного класса. При этом значения назначаются свойствам объекта ДО вызова конструктора. Если свойства с именами, соответствующими названиям столбцов, не существуют, они будут созданы автоматически (с областью видимости public).
Если ваши данные нуждаются в обязательной обработке сразу после их получения из базы данных, ее можно реализовать в конструкторе класса.
Для примера возьмем ситуацию, когда вам нужно скрыть часть адреса проживания человека.
class secret_person {
public $name;
public $addr;
public $city;
public $other_data;
function __construct($other = '') {
$this->addr = preg_replace('/[a-z]/', '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 (это огромный модуль!), оставить без упоминания следующие несколько функций нельзя.
$DBH->lastInsertId();
Метод ->lastInsertId() возвращает id последней вставленной записи. Стоит заметить, что он всегда вызывается у объекта базы данных (в статье он именуется $DBH), а не объекта с выражением ($STH).
$DBH->exec('DELETE FROM folks WHERE 1');
$DBH->exec("SET time_zone = '-8:00'");
Метод ->exec() используется для операций, которые не возвращают никаких данных, кроме количества затронутых ими записей.
$safe = $DBH->quote($unsafe);
Метод ->quote() ставит кавычки в строковых данных таким образом, что их становится безопасно использовать в запросах. Пригодится, если вы не используете prepared statements.
$rows_affected = $STH->rowCount();
Метод ->rowCount() возвращает количество записей, которые поучаствовали в операции. К сожалению, эта функция отказывалась работать с SELECT-запросами вплоть до PHP 5.1.6. Если обновить версию PHP не представляется возможным, количество записей можно получить так:
$sql = "SELECT COUNT(*) FROM folks";
if ($STH = $DBH->query($sql)) {
# проверяем количество записей
if ($STH->fetchColumn() > 0) {
# делаем здесь полноценную выборку, потому что данные найдены!
}
else {
# выводим сообщение о том, что удовлетворяющих запросу данных не найдено
}
}
Заключение
Надеюсь, этот материал поможет кому-то из вас осуществить миграцию с расширений mysql и mysqli.
PDO Exception Questions — How to Catch Them
As a rule —
DO NOT catch them.
For example, your code here should be written this way
$stmt = $db->prepare("INSERT INTO tbl_user (id, name, password, question, answer) VALUES (NULL, :name, :password, :question, :answer)");
$stmt->bindValue(":name", $_POST['name']);
$stmt->bindValue(":password", $_POST['password']);
$stmt->bindValue(":question", $_POST['question']);
$stmt->bindValue(":answer", $_POST['answer']);
$stmt->execute();
echo "Successfully added the new user " . $_POST['name'];
without any try or catch calls. Because you have no particular scenario for handling an exception here (a simple echo is scarcely counts as a handling scenario).
Instead, let it bubble up to the application-wide error handler (don’t be scared by the term, PHP already has a built-in one).
However, I’m having issues catching the errors how I’d like (errors like «Duplicate Entry», «Null Value» etc in MySQL).
Only in case if you have a certain scenario, you have to use try-catch operator, but you have to always check, whether the error you’ve got is one you expected. Otherwise an exception have to be re-thrown:
try {
$pdo->prepare("INSERT INTO users VALUES (NULL,?,?,?,?)")->execute($data);
} catch (PDOException $e) {
if ($e->getCode() == 1062) {
// Take some action if there is a key constraint violation, i.e. duplicate name
} else {
throw $e;
}
}
and of course (as it turned out to be the vere problem for this question), you have to set up PDO in exception mode, either in a constructor parameter of simply by adding the code
$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
right after connect.
PDO Exception Questions — How to Catch Them
As a rule —
DO NOT catch them.
For example, your code here should be written this way
$stmt = $db->prepare("INSERT INTO tbl_user (id, name, password, question, answer) VALUES (NULL, :name, :password, :question, :answer)");
$stmt->bindValue(":name", $_POST['name']);
$stmt->bindValue(":password", $_POST['password']);
$stmt->bindValue(":question", $_POST['question']);
$stmt->bindValue(":answer", $_POST['answer']);
$stmt->execute();
echo "Successfully added the new user " . $_POST['name'];
without any try or catch calls. Because you have no particular scenario for handling an exception here (a simple echo is scarcely counts as a handling scenario).
Instead, let it bubble up to the application-wide error handler (don’t be scared by the term, PHP already has a built-in one).
However, I’m having issues catching the errors how I’d like (errors like «Duplicate Entry», «Null Value» etc in MySQL).
Only in case if you have a certain scenario, you have to use try-catch operator, but you have to always check, whether the error you’ve got is one you expected. Otherwise an exception have to be re-thrown:
try {
$pdo->prepare("INSERT INTO users VALUES (NULL,?,?,?,?)")->execute($data);
} catch (PDOException $e) {
if ($e->getCode() == 1062) {
// Take some action if there is a key constraint violation, i.e. duplicate name
} else {
throw $e;
}
}
and of course (as it turned out to be the vere problem for this question), you have to set up PDO in exception mode, either in a constructor parameter of simply by adding the code
$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
right after connect.