Ошибки и их обработка
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.
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDO::errorInfo —
Fetch extended error information associated with the last operation on the database handle
Description
public PDO::errorInfo(): array
Parameters
This function has no parameters.
Return Values
PDO::errorInfo() returns an array of error information
about the last operation performed by this database handle. The array
consists of at least the following fields:
| Element | Information |
|---|---|
| 0 | SQLSTATE error code (a five characters alphanumeric identifier defined in the ANSI SQL standard). |
| 1 | Driver-specific error code. |
| 2 | Driver-specific error message. |
Note:
If the SQLSTATE error code is not set or there is no driver-specific
error, the elements following element 0 will be set tonull.
PDO::errorInfo() only retrieves error information for
operations performed directly on the database handle. If you create a
PDOStatement object through PDO::prepare() or
PDO::query() and invoke an error on the statement
handle, PDO::errorInfo() will not reflect the error
from the statement handle. You must call
PDOStatement::errorInfo() to return the error
information for an operation performed on a particular statement handle.
Examples
Example #1 Displaying errorInfo() fields for a PDO_ODBC connection to a DB2 database
<?php
/* Provoke an error -- bogus SQL syntax */
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
echo "nPDO::errorInfo():n";
print_r($dbh->errorInfo());
}
?>
The above example will output:
PDO::errorInfo():
Array
(
[0] => HY000
[1] => 1
[2] => near "bogus": syntax error
)
See Also
- PDO::errorCode() — Fetch the SQLSTATE associated with the last operation on the database handle
- PDOStatement::errorCode() — Fetch the SQLSTATE associated with the last operation on the statement handle
- PDOStatement::errorInfo() — Fetch extended error information associated with the last operation on the statement handle
alagar86 at gmail dot com ¶
12 years ago
Please note : that this example won't work if PDO::ATTR_EMULATE_PREPARES is true.
You should set it to false
<?php
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
echo "nPDO::errorInfo():n";
print_r($dbh->errorInfo());
}
?>
quickshiftin at gmail dot com ¶
15 years ago
here are the error codes for sqlite, straight from their site:
The error codes for SQLite version 3 are unchanged from version 2. They are as follows:
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
#define SQLITE_PERM 3 /* Access permission denied */
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
#define SQLITE_BUSY 5 /* The database file is locked */
#define SQLITE_LOCKED 6 /* A table in the database is locked */
#define SQLITE_NOMEM 7 /* A malloc() failed */
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
#define SQLITE_FULL 13 /* Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA 17 /* The database schema changed */
#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
#define SQLITE_MISMATCH 20 /* Data type mismatch */
#define SQLITE_MISUSE 21 /* Library used incorrectly */
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* Authorization denied */
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
mazen at mindcraftinc dot com ¶
14 years ago
Some PDO drivers return a larger array. For example, the SQL Server driver returns 5 values.
For example:
<?php
$numRows = $db->exec("DELETE FROM [TableName] WHERE ID between 6 and 17");
print_r($db->errorInfo());
?>
Result:
Array
(
[0] => 00000
[1] => 0
[2] => (null) [0] (severity 0) []
[3] => 0
[4] => 0
)
You need to set the error mode attribute PDO::ATTR_ERRMODE to PDO::ERRMODE_EXCEPTION.
And since you expect the exception to be thrown by the prepare() method you should disable the PDO::ATTR_EMULATE_PREPARES* feature. Otherwise the MySQL server doesn’t «see» the statement until it’s executed.
<?php
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->prepare('INSERT INTO DoesNotExist (x) VALUES (?)');
}
catch(Exception $e) {
echo 'Exception -> ';
var_dump($e->getMessage());
}
prints (in my case)
Exception -> string(91) "SQLSTATE[42S02]: Base table or view not found:
1146 Table 'test.doesnotexist' doesn't exist"
see http://wezfurlong.org/blog/2006/apr/using-pdo-mysql/
EMULATE_PREPARES=true seems to be the default setting for the pdo_mysql driver right now.
The query cache thing has been fixed/change since then and with the mysqlnd driver I hadn’t problems with EMULATE_PREPARES=false (though I’m only a php hobbyist, don’t take my word on it…)
*) and then there’s PDO::MYSQL_ATTR_DIRECT_QUERY — I must admit that I don’t understand the interaction of those two attributes (yet?), so I set them both, like
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
PDO::ATTR_EMULATE_PREPARES=>false,
PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
Вернуться к: 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
Содержание
- PDOStatement::errorInfo
- Description
- Parameters
- Return Values
- Examples
- See Also
- User Contributed Notes 4 notes
- PDO::errorCode
- Описание
- Список параметров
- Возвращаемые значения
- Примеры
- Смотрите также
- PDO::errorInfo
- Описание
- Список параметров
- Возвращаемые значения
- Примеры
- Смотрите также
- User Contributed Notes 3 notes
- Ошибки и их обработка
- MySQL Functions (PDO_MYSQL)
- Introduction
- Installation
- Predefined Constants
- Runtime Configuration
- Table of Contents
- User Contributed Notes 8 notes
PDOStatement::errorInfo
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDOStatement::errorInfo — Fetch extended error information associated with the last operation on the statement handle
Description
Parameters
This function has no parameters.
Return Values
PDOStatement::errorInfo() returns an array of error information about the last operation performed by this statement handle. The array consists of at least the following fields:
| Element | Information |
|---|---|
| SQLSTATE error code (a five characters alphanumeric identifier defined in the ANSI SQL standard). | |
| 1 | Driver specific error code. |
| 2 | Driver specific error message. |
Examples
Example #1 Displaying errorInfo() fields for a PDO_ODBC connection to a DB2 database
/* Provoke an error — the BONES table does not exist */
$sth = $dbh -> prepare ( ‘SELECT skull FROM bones’ );
$sth -> execute ();
echo «nPDOStatement::errorInfo():n» ;
$arr = $sth -> errorInfo ();
print_r ( $arr );
?>
The above example will output:
See Also
- PDO::errorCode() — Fetch the SQLSTATE associated with the last operation on the database handle
- PDO::errorInfo() — Fetch extended error information associated with the last operation on the database handle
- PDOStatement::errorCode() — Fetch the SQLSTATE associated with the last operation on the statement handle
User Contributed Notes 4 notes
Seriously errorInfo is your friend. Use it.
If these look like your google searches then you need errorInfo
«no database error showing in php»
«pdo selects from database but wont insert»
«pdo insert not working»
«isnt pdo just a big hype, should I go back to mysql?»
«how much do surgeons make?»
Trust me it will definitely save you hours of insanity if you make it a habit to use it in development. Forget E-ALL, it failed me since well, E-ALL apparently doesn’t know that I didn’t set a default value in my MySQL table and my query wasnt adding anything to it. So always do this
= ‘do something on a mysql table where foo = :bar’ ;
$stmt = prepare ( $sql );
$stmt -> bindValue ( ‘:bar’ , $foo , PDO :: PARAM_ [ DATA TYPE ]);
$stmt -> execute ();
// very important during development. But take it off in production
$foo_arr = $stmt -> errorInfo ();
print_r ( $foo_arr );
//Sample print_r return
/*
Array(
[0] => HY000
[1] => 1364
[2] => Field ‘phone’ doesn’t have a default value
)
Never have I been so happy to see an error
*/
?>
While its common practice for any decent developer to always watch out and try to catch for errors, even the best of us make mistakes. This is not a replacement for exceptions, but the simplicity is priceless.
Источник
PDO::errorCode
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDO::errorCode — Возвращает код SQLSTATE результата последней операции с базой данных
Описание
Список параметров
У этой функции нет параметров.
Возвращаемые значения
Возвращает SQLSTATE — пятисимвольный идентификатор, определённый в стандарте ANSI SQL-92. Первые два символа SQLSTATE отвечают за класс ошибки, а следующие три определяют её подкласс. Класс ошибок 01 означает предупреждение, которому сопутствует возвращаемый код SQL_SUCCESS_WITH_INFO. Классы отличные от 01, за исключением ‘IM’, означают ошибки выполнения запросов к базе данных. Класс ‘IM’ свидетельствует об ошибках и предупреждениях, которые вызваны самой реализацией PDO (или, возможно, ODBC, если используется драйвер ODBC). Значение подкласса ‘000’ в любом классе означает, что подкласс для этого SQLSTATE отсутствует.
PDO::errorCode() выдаёт код ошибки только для операций, совершаемых с базой данных напрямую из PDO. Если создать объект PDOStatement методами PDO::prepare() или PDO::query() , и вызвать ошибку его методами, PDO::errorCode() эту ошибку не отобразит. Вам нужно вызвать PDOStatement::errorCode() , чтобы получить код ошибки для операции, выполняемой на определённом объекте PDOStatement.
Возвращает null , если никаких операций над базой данных средствами PDO-объекта не производилось.
Примеры
Пример #1 Получение кода SQLSTATE
/* Спровоцируем ошибку — таблицы BONES не существует */
$dbh -> exec ( «INSERT INTO bones(skull) VALUES (‘lucy’)» );
echo «nPDO::errorCode(): » , $dbh -> errorCode ();
?>
Результат выполнения данного примера:
Смотрите также
- PDO::errorInfo() — Получает расширенную информацию об ошибке, произошедшей в ходе последнего обращения к базе данных
- PDOStatement::errorCode() — Получает код SQLSTATE, связанный с последней операцией в объекте PDOStatement
- PDOStatement::errorInfo() — Получение расширенной информации об ошибке, произошедшей в результате работы объекта PDOStatement
Источник
PDO::errorInfo
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDO::errorInfo — Получает расширенную информацию об ошибке, произошедшей в ходе последнего обращения к базе данных
Описание
Список параметров
У этой функции нет параметров.
Возвращаемые значения
PDO::errorInfo() возвращает массив с информацией об ошибке, произошедшей в ходе выполнения последней операции с базой данных. Массив содержит как минимум следующие поля:
| Элемент | Информация |
|---|---|
| Код ошибки SQLSTATE (пятисимвольный идентификатор, определённый в стандарте ANSI SQL). | |
| 1 | Код ошибки, заданный драйвером. |
| 2 | Сообщение об ошибке, заданное драйвером |
Если не задан SQLSTATE код или драйвер не сообщил об ошибке, то элементы следующие за нулевым будут иметь значение null .
PDO::errorInfo() выдаёт информацию об ошибке только для операций, совершаемых с базой данных напрямую из PDO. Если создать объект PDOStatement методами PDO::prepare() или PDO::query() , и вызвать ошибку его методами, PDO::errorInfo() эту ошибку не отобразит. Вам нужно вызвать PDOStatement::errorInfo() , чтобы получить информации об ошибках для операции, выполняемой на определённом объекте PDOStatement.
Примеры
Пример #1 Вывод полей массива errorInfo() для PDO_ODBC подключения к базе данных DB2
Результат выполнения данного примера:
Смотрите также
- PDO::errorCode() — Возвращает код SQLSTATE результата последней операции с базой данных
- PDOStatement::errorCode() — Получает код SQLSTATE, связанный с последней операцией в объекте PDOStatement
- PDOStatement::errorInfo() — Получение расширенной информации об ошибке, произошедшей в результате работы объекта PDOStatement
User Contributed Notes 3 notes
Please note : that this example won’t work if PDO::ATTR_EMULATE_PREPARES is true.
You should set it to false
-> setAttribute ( PDO :: ATTR_EMULATE_PREPARES , false );
$stmt = $dbh -> prepare ( ‘bogus sql’ );
if (! $stmt ) <
echo «nPDO::errorInfo():n» ;
print_r ( $dbh -> errorInfo ());
>
?>
here are the error codes for sqlite, straight from their site:
The error codes for SQLite version 3 are unchanged from version 2. They are as follows:
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
#define SQLITE_PERM 3 /* Access permission denied */
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
#define SQLITE_BUSY 5 /* The database file is locked */
#define SQLITE_LOCKED 6 /* A table in the database is locked */
#define SQLITE_NOMEM 7 /* A malloc() failed */
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
#define SQLITE_FULL 13 /* Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA 17 /* The database schema changed */
#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
#define SQLITE_MISMATCH 20 /* Data type mismatch */
#define SQLITE_MISUSE 21 /* Library used incorrectly */
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* Authorization denied */
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
Some PDO drivers return a larger array. For example, the SQL Server driver returns 5 values.
For example:
= $db -> exec ( «DELETE FROM [TableName] WHERE ID between 6 and 17» );
print_r ( $db -> errorInfo ());
?>
Result:
Array
(
[0] => 00000
[1] => 0
[2] => (null) [0] (severity 0) []
[3] => 0
[4] => 0
)
Источник
Ошибки и их обработка
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» );
?>
Источник
MySQL Functions (PDO_MYSQL)
Introduction
PDO_MYSQL is a driver that implements the PHP Data Objects (PDO) interface to enable access from PHP to MySQL databases.
PDO_MYSQL uses emulated prepares by default.
When running a PHP version before 7.1.16, or PHP 7.2 before 7.2.4, set MySQL 8 Server’s default password plugin to mysql_native_password or else you will see errors similar to The server requested authentication method unknown to the client [caching_sha2_password] even when caching_sha2_password is not used.
This is because MySQL 8 defaults to caching_sha2_password, a plugin that is not recognized by the older PHP (mysqlnd) releases. Instead, change it by setting default_authentication_plugin=mysql_native_password in my.cnf . The caching_sha2_password plugin will be supported in a future PHP release. In the meantime, the mysql_xdevapi extension does support it.
Beware: Some MySQL table types (storage engines) do not support transactions. When writing transactional database code using a table type that does not support transactions, MySQL will pretend that a transaction was initiated successfully. In addition, any DDL queries issued will implicitly commit any pending transactions.
The MySQL driver does not properly support PDO::PARAM_INPUT_OUTPUT via PDOStatement::bindParam() ; while such parameters can be used, they are not updated (i.e. the actual output is ignored).
Installation
The common Unix distributions include binary versions of PHP that can be installed. Although these binary versions are typically built with support for the MySQL extensions, the extension libraries themselves may need to be installed using an additional package. Check the package manager that comes with your chosen distribution for availability.
For example, on Ubuntu the php5-mysql package installs the ext/mysql, ext/mysqli, and PDO_MYSQL PHP extensions. On CentOS, the php-mysql package also installs these three PHP extensions.
Alternatively, you can compile this extension yourself. Building PHP from source allows you to specify the MySQL extensions you want to use, as well as your choice of client library for each extension.
When compiling, use —with-pdo-mysql[=DIR] to install the PDO MySQL extension, where the optional [=DIR] is the MySQL base library. Mysqlnd is the default library. For details about choosing a library, see Choosing a MySQL library.
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.
SSL support is enabled using the appropriate PDO_MySQL constants, which is equivalent to calling the » MySQL C API function mysql_ssl_set(). Also, SSL cannot be enabled with PDO::setAttribute because the connection already exists. See also the MySQL documentation about » connecting to MySQL with SSL.
Predefined Constants
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 ( int ) 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
PDO::MYSQL_ATTR_LOCAL_INFILE ( int )
Enable LOAD LOCAL INFILE .
Note, this constant can only be used in the driver_options array when constructing a new database handle.
PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY ( string )
Allows restricting LOCAL DATA loading to files located in this designated directory. Available as of PHP 8.1.0.
Note, this constant can only be used in the driver_options array when constructing a new database handle.
PDO::MYSQL_ATTR_INIT_COMMAND ( string )
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 ( int )
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 ( int )
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 ( int )
Maximum buffer size. Defaults to 1 MiB. This constant is not supported when compiled against mysqlnd.
PDO::MYSQL_ATTR_DIRECT_QUERY ( int )
Perform direct queries, don’t use prepared statements.
PDO::MYSQL_ATTR_FOUND_ROWS ( int )
Return the number of found (matched) rows, not the number of changed rows.
PDO::MYSQL_ATTR_IGNORE_SPACE ( int )
Permit spaces after function names. Makes all functions names reserved words.
PDO::MYSQL_ATTR_COMPRESS ( int )
Enable network communication compression.
PDO::MYSQL_ATTR_SSL_CA ( int )
The file path to the SSL certificate authority.
PDO::MYSQL_ATTR_SSL_CAPATH ( int )
The file path to the directory that contains the trusted SSL CA certificates, which are stored in PEM format.
PDO::MYSQL_ATTR_SSL_CERT ( int )
The file path to the SSL certificate.
PDO::MYSQL_ATTR_SSL_CIPHER ( int )
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
PDO::MYSQL_ATTR_SSL_KEY ( int )
The file path to the SSL key.
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT ( int )
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 ( int )
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.
Runtime Configuration
The behaviour of these functions is affected by settings in php.ini .
PDO_MYSQL Configuration Options
| Name | Default | Changeable |
|---|---|---|
| pdo_mysql.default_socket | «/tmp/mysql.sock» | PHP_INI_SYSTEM |
| pdo_mysql.debug | NULL | PHP_INI_SYSTEM |
For further details and definitions of the PHP_INI_* modes, see the Where a configuration setting may be set.
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.
Enables debugging for PDO_MYSQL. This setting is only available when PDO_MYSQL is compiled against mysqlnd and in PDO debug mode.
Table of Contents
User Contributed Notes 8 notes
There is an important undocumented attribute which disables certificate CN verification available after
5.6.22 (not sure), 7.0.18 (verified) and 7.1.15 (not sure)
possible values: true, false
default value: true
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. .
After spending hours trying to track down why we were getting this error on a new server, after the same code ran fine on other servers, we found the problem to be an old MySQL _client_ library running on our web server, and a latest-version MySQL _server_ running on the database server’s box.
Upgraded the MySQL client on the web server to the current revision and the problem went away.
To use «PDO::MYSQL_ATTR_USE_BUFFERED_QUERY» you should call
PDO::setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
It will not work when passed into PDO::prepare()
Today’s PHP snapshot now has SSL support for PDO. Follow the directions here ( http://dev.mysql.com/doc/refman/5.0/en/secure-create-certs.html ) to set up MySQL and then use the following connection options:
= new PDO (
‘mysql:host=hostname;dbname=ssldb’ ,
‘username’ ,
‘password’ ,
array(
PDO :: MYSQL_ATTR_SSL_KEY => ‘/path/to/client-key.pem’ ,
PDO :: MYSQL_ATTR_SSL_CERT => ‘/path/to/client-cert.pem’ ,
PDO :: MYSQL_ATTR_SSL_CA => ‘/path/to/ca-cert.pem’
)
);
?>
I have been getting the error below when performing multiple queries within a single page.
Setting the attribute below did not seem to work for me.
So building on previous example i am initilizing my stmt variable on every query and a fetch all into an array. Seems to be working for me.
Error:
PDO Error 1.1: Array ( [0] => xxx[1] => yyy[2] => Lost connection to MySQL server during query )
false));
$stmt = $dbh->prepare(«CALL getname()»);
// call the stored procedure
$stmt->execute();
// fetch all rows into an array.
$rows = $stmt->fetchAll();
foreach ($rows as $rs)
<
$id = $rs[‘id’];
>
//initilise the statement
unset($stmt);
$stmt = $dbh->prepare(«call secondprocedure(?);»);
$stmt->bindValue(1, $id);
if ( ! $stmt->execute() )
<
echo «PDO Error 1.1:n»;
print_r($stmt->errorInfo());
exit;
>
unset($stmt);
> catch (PDOException $e) <
print «Error!: » . $e->getMessage() . «
«;
die();
>
?>
SQLSTATE[HY000]: 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.
= 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’;
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’;
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 );
?>
This will run fine but PDO will balk with the ‘unbuffered’ error if you follow this with another query.
= 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 = 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 = 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.
If you have the error ‘could not find driver’ and won’t able to install driver for newer linux(Fedora, Redhat, CentOS) with latest PHP7/MariaDB10/Apache2.4 get this package installed and get every things smooth.
First download latest version of epel and remi for your distribution. This links is for Fedora 26:
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
Источник
The first step is to switch on exceptions. When bootstrapping PDO, make sure to do:
$opts = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_BOTH, PDO::ATTR_EMULATE_PREPARES => false, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false ]; $dbh = new PDO( "mysql:host=$host;port=$port;dbname=$name;charset=utf8", $user, $pass, $opts );
Every time you execute a PDO statement using PDOStatement::execute.
There is a possiblity of an exception being raised.
use PDOException; try { $sth = $dbh->prepare('...'); $sth->execute(); } catch (PDOExcepton $e) { var_dump($e->getCode()); var_dump($e->getMessage()); var_dump($e->errorInfo); }
In the above we demonstrate the 3 most important methods and properties of the exception object.
The returned getCode() gives back a 5 character SQLSTATE code string. This is defined as part of the SQL standard and is meant to be generic across databases. For more information on this code: https://en.wikibooks.org/wiki/Structured_Query_Language/SQLSTATE
The returned getMessage() is a string construced by PDO which has a format of:
SQLSTATE[XXXXX]: DESCRIPTION: YYYY MESSAGE
Where XXXXX is the SQLSTATE code, DESCRIPTION is a PHP supplied description like Syntax error or access violation or <<Unknown error>>, YYYY is the driver specific code, and finally the MESSAGE is the driver returned message.
Driver specific codes are usually more specific than the SQLSTATE code, so you can pattern match on driver specific code in order to dig down to more specific errors. For example this is the list of MySQL driver error codes and how they match to SQLSTATE codes: https://dev.mysql.com/doc/refman/en/error-messages-server.html and here’s the one for MariaDB: https://mariadb.com/kb/en/mariadb/mariadb-error-codes/
It is possible to create custom SQLSTATE codes to use for schema-specific errors that your application should handle. In MySQL, you should use the DECLARE ... CONDITION syntax to give human readable names to your custom error codes. Custom error codes should avoid conflicting with existing error codes. The standard SQL specification says that classes beginning with 0, 1, 2, 3, 4, A, B, C, D, E, F and G are reserved for standard-defined classes, while other classes are vendor-specific. It also says that, when the class is standard-defined, subclasses starting with those characters (except for ‘000’) are standard-defined subclasses, while other subclasses are vendor-defined. So depending on your database, look for a range that isn’t used now and unlikely to be used in the future. Here’s an example use: 50000 for general application errors, and then start at 50001 for specific errors.
I have not found good documentation on what kind of PHP supplied descriptions are available. You can use it as kind of summary description of the error. However you have to use regex to extract it from the message string as it is not discretely provided by other means.
When you use database triggers to enforce constraints, you will probably use the 45000 SQLSTATE code, you can pattern match on 45000 to know whether it’s a trigger error or otherwise.
In order to do pattern matching easily, rather than using getMessage() or getCode(), you should instead use the errorInfo array. This provides the SQLSTATE code (still in string type), the driver specific code (in my MySQL tests, the type was integer), and the driver specific message. If you do pattern matching on the SQLSTATE code, your codebase will be somewhat more portable than pattern matching on the driver specific code.
Here is an example of pattern matching against the PDO errors. Make sure to read your database manual and know what kind of errors you’re interested in. Note that in PHP it’s possible to compare strings and integers for equality.
try { // ... } catch (PDOException $e) { switch ($e->errorInfo[1]) { case 1062: error_log('Violating unique key constraint: ' . $e->errorInfo[2]); case 1644: // for mysql this is the same as matching on getCode() or errorInfo[0] using '45000' error_log('User Defined Error!: ' . $e->errorInfo[2]); default: throw $e; } }
If the above code exists in some model, you can then rethrow and wrap the exception in a model specific exception, or handle the exception there exactly.
If you’re using an ORM that wraps the PDOException, you should always be able to get it using getPrevious().
Handling PDO exceptions properly, can allow a clean propagation of errors from the database to the frontend of a web application, as this allows you to keep the constraints on data where the data lives and is ultimately managed. Then we avoid having to repeat validation logic from database to application.
However it may be even cleaner if the schema and constraints on the data were a commonly understood interface that was imported by frontend, backend and database, and validation code and database schema and database model operations can be automatically generated from the common schema. (Perhaps JSON Schema or schema.org or PostgREST might rescue us from CRUD hell?)
В этой статье разговор пойдет о 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 API для работы с базой данных.
Что?
Для соединения с базой данных MySQL имеется три разных API:
mysqlmysqli– MySQL Improved (улучшенная)pdo– PHP Data Objects (объекты данных PHP)
Традиционный API mysql хорошо выполняет свою работу и получил широкое распространение благодаря тому, что делает процесс получения записей из базы данных очень простым. Например:
/*
* Демонстрация старого способа работы с базой данных MySQL
*/
# Соединение
mysql_connect('localhost', 'username', 'password') or die('Could not connect: ' . mysql_error());
# Выбор базы данных
mysql_select_db('someDatabase') or die('Не могу выбрать базу данных');
# Выполнение запроса
$query = "SELECT * from someTable";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
# Фильтрация строк и вывод нужной информации
while ($row = mysql_fetch_object($result)) {
echo $row->name;
}
Да, приведенный пример очень прост. Но у него есть существенные недостатки:
- Устарел: хотя официально его не признают устаревшим, но для новых приложений и обучения лучше использовать другие API.
- Очистка: процесс очистки ввода пользователя от ненужного кода ложится на плечи разработчика.
- Гибкость: данный API не гибкий и работает только с базой данных MySQL. А если нужно перейти на другую?
PDO (PHP Data Objects) предоставляет более мощный инструмент для использования. Не нужно думать о драйвере базы данных, можно использовать подготовленные выражения, исключаются инъекции кода SQL.
Как?
На первый взгляд PDO API выглядит устрашающе. Не потому что он чрезмерно сложен, а потому что mysql является очень простым! Нужно просто сделать несколько несложных действий.
Соединение
Вы уже знаете устаревший способ соединения с базой данной MySQL:
# Соединение
mysql_connect('localhost', 'username', 'password') or die('Could not connect: ' . mysql_error());
При работе с PDO нужно создать новый экземпляр класса, указать драйвер, имя базы данных, имя пользователя и пароль:
$conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
Ничего сложного.
Преимуществом данного подхода является простота изменения базы данных. Достаточно поменять имя источника данных. Нет зависимости от MySQL как при использовании функции mysql_connect.
Ошибки
Но что если произойдет ошибка при соединении с базой данных? Обернем все в блок try/catch:
try {
$conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
Обратите внимание на то, что по умолчанию для PDO установлен режим работы с ошибками PDO::ERRMODE_SILENT. Если все оставить без изменении, то придется вручную обрабатывать ошибки после выполнения запроса.
echo $conn->errorCode(); echo $conn->errorInfo();
При разработке лучше устанавливать режим PDO::ERRMODE_EXCEPTION, в котором необработанные ошибки будут вызывать исключения и остановку скрипта.
Доступны следующие режимы работы с ошибками:
PDO::ERRMODE_SILENTPDO::ERRMODE_WARNINGPDO::ERRMODE_EXCEPTION
Получаем данные
Итак, у нас есть соединение с базой данных и теперь нужно получить данные. Есть два способа решить поставленную задачу: query и execute. Рассмотрим оба.
Query
/*
* Метод query
*/
$name = 'Joe'; # user-supplied data
try {
$conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$data = $conn->query('SELECT * FROM myTable WHERE name = ' . $conn->quote($name));
foreach($data as $row) {
print_r($row);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
Хотя выше приведенный пример будет работать, нам нужно вручную очищать данные пользователя с помощью метода PDO::quote. Данный метод эквивалентен функции mysql_real_escape_string (в обоих случаях строка очищается и заключается в кавычки). В случаях, когда нужно построить запрос SQL с использованием пользовательских данных, лучше применять подготовленные выражения. То есть, если запрос SQL не зависит от ввода пользователя, метод query будет отличным выбором, а результат можно обработать выражением foreach.
Execute
/*
* Метод execute
*/
$id = 5;
try {
$conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$stmt->execute(array('id' => $id));
while($row = $stmt->fetch()) {
print_r($row);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
В данном примере используется также метод prepare для подготовки запроса, перед тем, как присоединить данные пользователя. В таком случае инъекция SQL теоретически невозможна, так как сами данные не вставляются в текст запроса SQL. Используется именованный параметр (:id) для обозначения места.
Затем выполняется запрос с передачей массива, который содержит данные, которые следует использовать на месте именованного параметра.
$stmt->execute(array('id' => $id));
В качестве альтернативы можно использовать метод bindParam:
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
Обрабатываем данные
После вызова метода execute есть несколько способов получить данные: массив (по умолчанию), объект и так далее. В выше приведенном примере используется режим по умолчанию: PDO::FETCH_ASSOC. Его легко поменять, если нужно:
while($row = $stmt->fetch(PDO::FETCH_OBJ)) {
print_r($row);
}
Теперь результат будет в виде объекта. Доступны следующие опции:
- PDO::FETCH_ASSOC: Возвращает массив.
- PDO::FETCH_BOTH: Возвращает массив, проиндексированный по колонке с именем.
- PDO::FETCH_BOUND: Возвращает TRUE и назначает значение столбцов в наборе результата связанным переменным PHP.
- PDO::FETCH_CLASS: Возвращает новый экземпляр указанного класса.
- PDO::FETCH_OBJ: Возвращает анонимный объект, с именами свойств, соответствующих столбцам.
Выше приведенный код не имеет обратной связи на случай отсутствия результата. Исправим ситуацию:
$stmt->execute(array('id' => $id));
# Получаем массив, который содержит все строки результата
$result = $stmt->fetchAll();
# Если получена одна или более строк...
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
echo "Ничего не найдено.";
}
Полный код будет выглядеть следующим образом:
$id = 5;
try {
$conn = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$stmt->execute(array('id' => $id));
$result = $stmt->fetchAll();
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
echo "Ничего не найдено.";
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
Многократное выполнение
PDO проявляет свою силу в случаях, когда надо выполнить один и тот же запрос SQL несколько раз, но с разными параметрами.
try {
$conn = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
# Подготавливаем запрос один раз
$stmt = $conn->prepare('INSERT INTO someTable VALUES(:name)');
$stmt->bindParam(':name', $name);
# Первое выполнение
$name = 'Keith';
$stmt->execute();
# Второе выполнение
$name = 'Steven';
$stmt->execute();
} catch(PDOException $e) {
echo $e->getMessage();
}
Как только запрос подготовлен, его можно выполнить несколько раз с разными параметрами. Выше приведенный код вставляет две строки в базу данных: одна с именем “Kevin,” а другая — “Steven.”
Типовые операции с базой данных (CRUD)
Рассмотрим выполнение типовых операций с данными (CRUD — Create, Read, Update, Delete создание, чтение, обновление, удаление). Обратите внимание, что код для них будет очень похожим.
Создание (Вставка)
try {
$pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('INSERT INTO someTable VALUES(:name)');
$stmt->execute(array(
':name' => 'Justin Bieber'
));
# Изменено строк?
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage();
Обновление
$id = 5;
$name = "Joe the Plumber";
try {
$pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('UPDATE someTable SET name = :name WHERE id = :id');
$stmt->execute(array(
':id' => $id,
':name' => $name
));
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
Удаление
$id = 5;
try {
$pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('DELETE FROM someTable WHERE id = :id');
$stmt->bindParam(':id', $id); // Воспользуемся методом bindParam
$stmt->execute();
echo $stmt->rowCount(); // 1
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
Результат в виде объекта
Одним из замечательных свойств PDO (а также и mysqli) является возможность представления результата запроса в виде экземпляра класса или объекта. Например:
class User {
public $first_name;
public $last_name;
public function full_name()
{
return $this->first_name . ' ' . $this->last_name;
}
}
try {
$pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result = $pdo->query('SELECT * FROM someTable');
# Выводим результат как объект
$result->setFetchMode(PDO::FETCH_CLASS, 'User');
while($user = $result->fetch()) {
# Вызываем наш метод full_name
echo $user->full_name();
}
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
Заключение
Если вы все еще используете старый API mysql для работы с базой данных, то следует остановиться. Несмотря на то, что он еще не считается устаревшим, код будет иметь более высокий уровень безопасности и поддержки, если перевести его на PDO.