Немного о компьютере

PHP PDO — работаем с базами данных правильно. Как настроить и использовать PDO (PHP Data Objects) для доступа к базе Работа с mysql основные функции особенности pdo

PDO (PHP Data Objects) - расширение для PHP, предоставляющее простой интерфейс для доступа к различным базам данных. Если говорить очень просто и коротко, при помощи PDO в PHP подключаются к базам данных разных типов.

В этом уроке мы будем подключаться к базе данных MySQL, так как это самая распространённая база данных.

Подключение к базе данных

Вы должны знать, что у сервера баз данных есть имя, также к нему могут подключаться пользователи, то есть для подключения должен быть использован логин и пароль.

Пример того, как мы можем подключиться к базе данных:

$db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);

Я думаю если вы интересуетесь PDO, то ваших знаний достаточно и этот синтаксис вам объяснять не нужно.

Итак, у нас есть объект подключения для доступа к базе данных.

Обработка исключений

Когда используется PDO, то ошибки подключений советуюю ловить при помощи конструкции try{...}catch{...} . Вот пример такого кода:

Try { $db = new PDO("myql:host=$host;dbname=$dbname", $user, $pass); } catch(PDOException $e) { echo "You have an error: ".$e->getMessage()."
"; echo "On line: ".$e->getLine(); }

Есть разные мнения, по поводу обработки ошибок, например не все советуют всегда использовать конструкцию try{...}catch{...} . Дело в том, что PHP и так выведет сообщение об ошибке на экран, так что этот код избыточен. Хотя если вы хотите например откатить транзакцию, то эта конструкция вам пригодится, но об этом ниже.

Получение данных из базы используя PDO, метод query

Для выборки из базы данных используется метод query , которому мы передаём строку запроса SQL.

$db->query("SELECT * FROM users");

Не забывайте, что для всех типов баз данных этот синтаксис сработает.

Также не забывайте про безопасность данных, передаваемых в SQL запросах. В PDO есть аналог функции mysql_real_escape_string() это метод quote .

$login = $db->quote($_POST["login"]); $sql = "SELECT * FROM users WHERE login = $login"; $result = $db->query($sql);

Обработка результата, методы FETCH и FETCHALL.

Теперь нам нужно преобразовать результат из переменной $res в массив. Это делается при помощи метода FETCH , которому передаётся константа.

$res = $db->query($sql); $result = $res->FETCH(PDO::FETCH_NUM); // нумерованный $result = $res->FETCH(PDO::FETCH_ASSOC); // ассоциативный $result = $res->FETCH(PDO::FETCH_BOTH); // ассоциативный и нумерованный вместе $result = $res->FETCH(PDO::FETCH_OBJ); // объектный тип $result = $res->FETCH(PDO::FETCH_LAZY); // сразу все типы

Очевидно, что константа FETCH_LAZY замедляет работу скрипта, поэтому её желательно не использовать.

Метод FETCH возвращает одну запись из результата. Если требуется получить все записи, нужно использовать метод FETCHALL . В дальнейшем результат, полученный в результате использования FETCHALL обрабаьываем в цикле foreach , как показано в примере:

$query = $db->query("SELECT * FROM users"); $result = $query->FETCHALL(PDO::FETCH_ASSOC); foreach($result as $arry) { echo $arry["name"] . "
"; }

Константа FETCH_CLASS

Особого объяснения требует константа FETCH_CLASS , она позволяет заполнить предварительно созданный класс данными из результата запроса к базе данных.

Давайте рассмотрим пример, с использованием константы FETCH_CLASS:

Class User { public $login; public $pass; public function showInfo() { echo "
" . $this->pass."
" . " : " . $this->login . "
"; } } $result = $stmt->FETCHALL(PDO::FETCH_CLASS, "User"); foreach($result as $user) { $user->showInfo(); }

Не забывайте важное правило - названия свойств в созданном классе обязаны быть такими же, как и названия полей в базе данных.

Подготовленные выражения

Подготовленные выражения нужно обязательно использовать если ваш SQL запрос содержит переменные.

Подготовленные выражения PDO - основная причина использовать PHP Data Objects, поскольку это единственный безопасный способ выполнения SQL запросов, в которых есть переменные, созданные пользователем.

Подготовленные выражения в PDO - это обычный SQL запрос, в котором переменная заменяется специальным маркером - плейсхолдером.

Именнованные плейсхолдеры

Сначала давайте рассмотрим именнованный плейсхолдер, его синтаксис например такой: :email .

Давайте рассмотрим пример запроса INSERT с использованием плейсхолдеров.

$stmt = $db->prepare("INSERT INTO messages (email, message) VALUES (:email, :message)");

В этом примере вместо переменных в запросе мы использовали два плейсхолдера (:email, :message)") .

$stmt = $db->prepare("INSERT INTO messages (email, message) VALUES (:email, :message)"); $stmt->bindParam(":email", $email); $stmt->bindParam(":message", message); $email = "Е-почта №1"; $message = "Какой-то текст сообщения"; $stmt->execute(); $email = "Е-почта №2"; $message = "Какой-то текст сообщения"; $stmt->execute();

Обратите внимание, что чтобы подготовить SQL запрос, мы пишем его в методе prepare() . Потом, чтобы указать к какому плейсхолдеру какую переменную привязать, используем метод bindParam() . Чтобы выполнить SQL запрос, мы вызываем метод execute() .

Итак, ещё раз последовательность работы с подготовленными выражениями по шагам:

  1. Присваиваем переменной $stmt результат выполнения метода prepare() .
  2. При помощи метода bindParam() мы связываем переменные и плейсхолдеры.
  3. Назначаем значения переменных.
  4. При помощи метода execute() выполняем запрос к базе данных.

Этот синтаксис можно записать

$stmt = prepare("SELECT name FROM users WHERE email = :email"); $stmt->execute(array("email" => $email));

Видно, что в метод execute() должен передаваться массив, в котором ключи должны совпадать с именами плейсхолдеров.

Кстати, у метода bindParam() есть синоним bindValue() .

Неименованные плейсхолдеры

Теперь рассмотрим работу с неименованными плейсхолдерами.

$stmt = prepare("SELECT name FROM users WHERE email = ?") $stmt->execute(array($email));

В этом синтаксисе вместо записи плейсхолдера:название указывается другая форма его записи - знак вопроса:? .

Тут значения массива $email будут поочерёдно присвоены плейсхолдерам:? , но в нашем примере плейсхолдер один.

Вот другой пример применения неименованных плейсхолдеров, с использованием метода bindParam() :

$stmt = $db->prepare("INSERT INTO articles (title, text) VALUES (?, ?)"); $stmt->bindParam(1, $email); $stmt->bindParam(2, $message); $email = "Е-почта №1"; $message = "Какой-то текст сообщения"; $stmt->execute(); $email = "Е-почта №2"; $message = "Какой-то текст сообщения"; $stmt->execute();

Вставка в базу данных, метод exec()

Если мы хотим что-нибудь записать в базу, тогда можно также использовать метод PDO::exec() .

$sql = "INSERT INTO (login, password) VALUES ($login, $password)"; $result = $db->exec($sql);

Если этот запрос будет выполнен, то в переменную $result попадёт количество затронутых в таблице строк.

PDO::exec() выполняет INSERT запросы, но не может получить данные из БД, этим занимается метод PDO::query() . PDO::exec() только запускает SQL запрос на выполнение и возвращает количество строк, задействованных в ходе его выполнения, он не возвращает результат выборки оператором SELECT .

PDO работает с базами данных. Если с каким-то типом базы данных не получается работать, идём в php.ini и ищем строчки начинающиеся с extension=php_pdo_{название базы данных}, и раскомментируем их.

Php для начинающих: Callback функции, урок 34! https://www.youtube.com/watch?v=2NwLHXUoXcw https://www.youtube.com/watch?v=GMzI6jR_bE4 https://www.youtube.com/watch?v=gFJsBQIqpto PHP урок 9 Рекурсия https://www.youtube.com/watch?v=gLAeJcKkd6c http://php.net/manual/ru/mysqli-result.fetch-array.php /* очищаем результаты выборки */ mysqli_free_result($result); /* закрываем подключение */ mysqli_close($link); http://myrusakov.ru/sql-osnovy.html Хорошая статья: https://ru.wikipedia.org/wiki/Join_(SQL) ООП PHP. Расширительные средства Глава "Расширительные средства" из книги Мэта Зандстра "PHP. Объекты, шаблоны и методики программирования". https://www.youtube.com/watch?v=6L2bxtTBCRo

http://phpfaq.ru/pdo#intro - тут хорошая статья. Есть про исключения важная информация.

Проще описано: http://myrusakov.ru/php-data-objects.html

Video: https://www.youtube.com/watch?v=ACUiBH5qV0U&list=PLr_acfJGVcirEijJXmKxj8QGkWkKb-Tj-&nohtml5=False

Фреймворк Bootstrap: быстрая адаптивная вёрстка

Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.

Верстайте на заказ и получайте деньги.

Бесплатный курс "Сайт на WordPress"

Хотите освоить CMS WordPress?

Получите уроки по дизайну и верстке сайта на WordPress.

Научитесь работать с темами и нарезать макет.

Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

*Наведите курсор мыши для приостановки прокрутки.

Назад Вперед

Основы работы с расширением PDO

Сегодня мы с вами разберём очень интересную тему - основы работы с расширением PDO для PHP.

PDO (PHP Data Objects) - это просто некий интерфейс, который позволяет работать с различными базами данных без учета их специфики. С помощью PDO мы можем легко переключаться между разными базами данных и управлять ими. Чтобы стало понятнее, давайте разберём пример.

Как мы должны были подключаться раньше к базе MySQL ?

Mysql_connect($host, $user, $password); mysql_select_db($db);

Чтобы подключиться к SQLite мы должны были написать так:

Sqlite_open($db);

Если нам нужна база данных PostgreSQL , то надо написать так:

Pg_connect("host=$host, dbname=$db, user=$user, password=$password");

Не очень-то удобно, верно? Получается, что если мы захотим сменить базу данных, то нам придется переделывать много кода. И вот, чтобы это исправить, появилось специальное PHP-расширение - PDO .

Давайте посмотрим, как мы теперь можем подключиться к базе:

$db = new PDO("mysql:host=$host;dbname=$db", $user, $password);

$db = new PDO("sqlite:$db);

PostgreSQL:

$db = new PDO("pgsql:host=$host;dbname=$db", $user, $password);

Как видите, всё то же самое, кроме строки подключения. Это единственное различие.


Теперь давайте рассмотрим, как раньше мы были должны выполнять запросы:

$sql = "INSERT INTO(name, email) VALUES($name, $email)"; // MySQL mysql_query($sql); // SQLite sqlite_query($sql); // PostgreSQL pg_query($sql);

Теперь же мы можем абстрагироваться от этого:

// PDO $result = $db->exec($sql);

Всё! Наш запрос будет выполнен независимо от того, какую БД мы используем , а в переменную result попадёт количество затронутых строк.

Однако выбрать что-то из базы данных таким способом мы не сможем. Для выборки нам нужно использовать не exec , а query .

$sql = "SELECT name FROM users"; $result = $db->query($sql);

Теперь давайте вспомним и о безопасности , ведь все данные нужно проверять. Как мы делали это раньше?

$sql = "SELECT * FROM users WHERE name = $name"; $name = $_POST["name"]; // MySQL $name = mysql_real_escape_string($name); // SQLite $name = sqlite_escape_string($name); // PostgreSQL $name = pg_escape_string($name);

Теперь же нам не нужно этого делать. PDO сделает всё за нас.

$name = $db->quote($name); $result = $db->query($sql);

PDO сам всё проверит и обработает переданные данные. Круто?:) Дальше ещё круче! Продолжим.

Как мы раньше преобразовывали результат в массив? Рассмотрим на примере базы MySQL .

$result = mysql_query($sql); // Так $row = mysql_fetch_assoc($result); // Или так... $row = mysql_fetch_array($result, FETCH_ASSOC);

Также, как и ассоциативный, мы могли получить и нумерованный массив. Теперь рассмотрим как это делается в PDO :

$stmt = $db->query($sql); // Ассоциативный $result = $stmt->FETCH(PDO::FETCH_ASSOC); // Нумерованный $result = $stmt->FETCH(PDO::FETCH_NUM); // Оба типа массивов одновременно $result = $stmt->FETCH(PDO::FETCH_BOTH); // Объект $result = $stmt->FETCH(PDO::FETCH_OBJ);

Использовать это также очень просто:

// Ассоциативный echo $result["name"]; // Нумерованный echo $result; // Объект echo $result->name;

Для "ленивых" есть такая вещь:

$stmt = $db->query($sql); $result = $stmt->FETCH(PDO::FETCH_LAZY);

Он возвращает сразу все 3 типа. Т.е. это FETCH_BOTH и FETCH_OBJ вместе. Как вы уже догадались, после этого доступ к данным можно получить любым из трех способов:

Echo $result->name; echo $result["name"]; echo $result;

Однако Fetch возвращает только одну запись, поэтому, если мы хотим получить все записи, то надо использовать FetchAll .

$stmt = $db->query("SELECT * FROM users"); $result = $stmt->FetchAll(PDO::FETCH_ASSOC); foreach($result as $user) { echo $user["name"]."
"; }

Но есть ещё одна классная штука, связанная с Fetch . С ее помощью мы можем заполнить наш класс данными из БД автоматически .

Class User { public $login; public $id; public function showInfo() { echo "".$this->id.""." : ".$this->login."
"; } } $db = new PDO("mysql:host=localhost;dbname=test", "root", ""); $stmt = $db->query("SELECT * FROM `users`"); $result = $stmt->fetchAll(PDO::FETCH_CLASS, "User"); foreach($result as $user) { $user->showInfo(); }

Как видите всё очень просто. Нам нужно просто указать константу FETCH_CLASS и через запятую в кавычках название класса, куда будут вставлены данные.

Потом перебираем в цикле объект и выводим нужную нам информацию.
Внимание! Названия свойств в классе должны совпадать с названиями полей в базе данных.

Помимо всего прочего, мы можем создавать так называемые подготовленные запросы . В чём их плюсы?

1. Мы можем один раз подготовить запрос, после чего запускать его столько раз, сколько нам нужно. Причём как с такими же, так и с другими параметрами.

Когда запрос подготовлен, СУБД анализирует его, компилирует и оптимизирует план его выполнения. В случае сложных запросов, время на выполнение будет ощутимо, если мы запускаем его с разными параметрами. В случае с подготовленными запросами это делается один раз и, следовательно, времени тратится меньше.

2. Параметры подготовленного запроса не требуется экранировать кавычками, драйвер делает это автоматически. Если в приложении используются только подготовленные запросы, то SQL-иньекции почти невозможны.

PDO может эмулировать подготовленные запросы , если они не поддерживаются драйвером. Теперь, давайте рассмотрим, как же их использовать?

$stmt = $db->prepare("INSERT INTO users (name, login) VALUES (:name, :login)"); $stmt->bindParam(":name", $name); $stmt->bindParam(":login", $login); // Вставим одну строку с такими значениями $name = "vasya"; $login = "vasya123"; $stmt->execute(); // Теперь другую строку с другими значениями $name = "petya"; $login = "petya123"; $stmt->execute();

Метод bindParam позволяет нам установить параметры. Думаю, тут всё понятно. Сначала там, где хотим, чтобы были вставлены данные, пишем такую строчку ":имя ". А затем указываем, откуда они будут браться. В данном случае они будут браться из переменных name и login .

Теперь мы можем использовать этот запрос с разными параметрами сколько угодно раз, а чтобы его выполнить, нужно вызвать метод execute . Это были именованные параметры. Также есть и не именованные .

$stmt = $db->prepare("INSERT INTO users (name, login) VALUES (?, ?)"); // Данные из переменной name будут вставлены вместо первого знака вопроса $stmt->bindParam(1, $name); // Данные из переменной login будут вставлены вместо второго знака вопроса $stmt->bindParam(2, $login); // Вставим одну строку с такими значениями $name = "vasya"; $login = "vasya123"; $stmt->execute(); // Теперь другую строку с другими значениями $name = "petya"; $login = "petya123"; $stmt->execute();

Следующий момент - как нам отлавливать ошибки?

Для этого есть класс PDOException . Я рекомендую все ваши запросы писать в блоке try-catch .

Try { $db = new PDO("myql:host=localhost;dbname=test", "root", ""); $stmt = $db->query("SELECT * FROM users"); $result = $stmt->fetch(PDO::FETCH_ASSOC); echo $result["login"]; } catch(PDOException $e) { echo "Ошибка: ".$e->getMessage()."
"; echo "На линии: ".$e->getLine(); }

Здесь мы допустили ошибку и написали myql вместо mysql . И класс PDOException нам об этом напишет.

У него несколько методов, но самые часто используемые это getMessage() , который возвращает нам текст ошибки и getLine() , который возвращает номер строки, на которой допущена ошибка.

Ну и напоследок поговорим о транзакциях . Сначала приведу код.

Try { $db = new PDO("mysql:host=localhost;dbname=test", "root", ""); $db->beginTransaction(); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login1")"); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login2")"); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login3")"); $db->commit(); } catch(PDOException $e) { $db->rollBack(); }

Здесь мы начинаем транзакцию с помощью метода beginTransaction() . Дальше идёт какой-то код запросов. Затем мы вызываем метод commit() , чтобы подтвердить наши изменения. Если же что-то пошло не так, то в блоке catch мы вызываем метод rollBack() , который вернёт все наши данные в предыдущее состояние.

"А зачем собственно нужны эти транзакции?" - спросите вы. Чтобы ответить на этот вопрос, рассмотрим пример, который я привёл выше. Там вы вставляем в поле "логин" значение login1, login2, login3 .

Представим, что после того, как вставились login1 и login2 , произошла какая-то ошибка. Получится, что эти данные вставлены, а login3 - нет. Во многих случаях это недопустимо и нарушит работу приложения в будущем.

Как раз для предотвращения таких ситуаций и нужны транзакции. Если наш скрипт дал сбой, то метод rollBack() вернёт всё в первоначальный вид. Т.е. login1 и login2 также не будут вставлены. Давайте сэмулируем эту ошибку.

Try { $db = new PDO("mysql:host=localhost;dbname=test", "root", ""); $db->beginTransaction(); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login1")"); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login2")"); exit("error"); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login3")"); $db->commit(); } catch(PDOException $e) { $db->rollBack(); }

После вставки login1 и login2 мы выходим из скрипта с помощью функции exit() . У нас выбрасывается исключение, мы попадаем в блок catch , а там мы возвращаем всё в первоначальный вид. Теперь, если мы посмотрим в базу данных, то не увидим там login1 и login2 .

На этом моменте мы закончим. Очевидно, что здесь мы разобрали далеко не всё, что предоставляет нам PDO, однако узнали основы работы с ним. Более подробную информацию о данном расширении вы всегда можете найти на официальном сайте PHP.

Материал подготовил Владислав Андреев специально для сайта сайт

P.S. Хотите двигаться дальше в освоении PHP и ООП? Обратите внимание на премиум-уроки по различным аспектам сайтостроения, включая программирование на PHP, а также на бесплатный курс по созданию своей CMS-системы на PHP с нуля с использованием ООП:

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Соединение с базой данных устанавливается тогда, когда создаётся экземпляр класса PDO. Не имеет значения, какой драйвер Вы хотите использовать; Вам всегда нужно будет использовать класс PDO. Его конструктор принимает параметры для того, чтобы определить источник базы данных (известный как DSN), и необязательные параметры для имени пользователя и пароля.

Соединение с MySQL:

$dbh = new PDO("mysql:host=localhost;dbname=test", $user, $pass);

Если произойдут какие либо ошибки соединения, то будет выброшено исключение: объект класса PDOException. Вы можете поймать его, если захотите обработать эту ситуацию, или можете оставить его для глобального обработчика исключений, который устанавливается через set_exception_handler ().

Обработка ошибок соединения:

try { $dbh = new PDO("mysql:host=localhost;dbname=test", $user, $pass); foreach($dbh->query(‘SELECT * from FOO’) as $row) { print_r($row); } $dbh = null; } catch (PDOException $e) { die("Ошибка! Всё. Приехали... ".$e->getMessage()); }

Внимание: Если Вы не поймаете исключение, брошенное конструктором PDO, действие по умолчанию, предпринятое движком zend, должно остановить скрипт и показать трассировку. Эта хрень выдаст все Ваши интимные подробности общения с базой данной. То есть покажет подробные детали соединения с базой данных, включая имя пользователя и пароль! На Вашей совести поймать это исключение, либо явно (через утверждение try catch ), либо неявно через set_exception_handler ().

После успешного соединения с базой данных оно остается активным на протяжении всей жизни экземпляра объекта PDO. Чтобы закрыть соединение, Вы должны разрушить объект, гарантируя, что все остающиеся на него ссылки удалены – сделать это можно, присвоив значение NULL переменной, которая содержит объект. Если Вы не сделаете этого явно, то PHP автоматически закроет соединение, когда скрипт завершит работу.

Закрытие соединения:

$dbh = new PDO("mysql:host=localhost;dbname=test", $user, $pass); // Здесь что то делаем: ... // А теперь, внимание: конец связи! $dbh = null;

Многие веб-приложения выигрывают от создания постоянных соединений с серверами баз данных. Постоянные соединения не закрываются, когда скрипт завершает работу, а кэшируются и снова используются, когда другой скрипт просит соединения, используя те же учётные данные для соединения. Кэш постоянных соединений позволяет избежать накладных расходов на установление нового соединения каждый раз, когда скрипт должен общаться с базой данных, в результате чего веб-приложения работают быстрее.

Установка постоянного соединения:

$dbh = new PDO("mysql:host=localhost;dbname=test", $user, $pass, array(PDO::ATTR_PERSISTENT => true));

Имейте ввиду: Если Вы хотите использовать постоянное соединение, Вы должны установить PDO::ATTR_PERSISTENT в массиве опций драйвера, который передаётся конструктору класса PDO. Устанавливая этот атрибут через PDO::setAttribute() после создания экземпляра объекта, драйвер не будет использовать постоянные связи. А так же, в этом случае вам не удастся расширить класс PDOStatement для каких то своих нужд, т.е. нельзя будет установить: PDO::ATTR_STATEMENT_CLASS

Обеспечивает методы для подготовки выражений и работы с объектами, которые могут сделать Вашу работу более продуктивной!

Введение в PDO

"PDO - PHP Data Objects - это уровень для доступа к базам данных, который обеспечивает унифицированные методы для доступа к различным базам данных."

Он не зависит от специфического синтаксиса базы данных и позволяет легко переключаться на другой тип баз данных и платформу простым изменением строки подключения в большинстве случаев.

Данный урок не является описанием процесса работы с SQL . Он предназначен для тех, кто использует расширения mysql или mysqli , чтобы помочь им перейти к более мощному и портируемому PDO.

Поддержка баз данных

Расширение поддерживает любую базу данных, для которой есть PDO драйвер. На текущий момент доступны драйвера для следующих типов баз данных:

  • 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 и win32 ODBC))
  • PDO_PGSQL (PostgreSQL)
  • PDO_SQLITE (SQLite 3 и SQLite 2)
  • 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 в блок try/catch и использовать механизм исключений. Обычно выполняется только одно подключение, в нашем примере показаны несколько подключений для отображения синтаксиса. $DBH содержит дескриптор базы данных и будет использоваться на протяжении всего нашего урока.

Вы можете закрыть любое соединение установкой дескриптора в null .

# Закрываем соединение $DBH = null;

Вы можете узнать больше о специфических опциях и строках подключения для различных баз данных из документов с сайта PHP.net .

Исключения и PDO

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);

Вне зависимости от установленного уровня контроля ошибка соединения всегда вызывает исключение и поэтому всегда должна быть заключена в блок try/catch .

PDO::ERRMODE_SILENT

Уровень контроля ошибок, устанавляваемы по умолчанию. На этом уровене ошибки генерируются по такому же принципу, как в расширениях mysql или mysqli . Два других уровня контроля ошибок более подходят для стиля програмирования в стиле DRY (Don"t Repeat Youself - не повторяй сам себя).

PDO::ERRMODE_WARNING

На данном уровне контроля ошибок генеррируются стандартные предупреждения PHP, при этом программа может продолжать выполение. Данный уровень удобен для отладки.

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"); } catch(PDOException $e) { echo " Извините. Но операция не может быть выполнена."; file_put_contents("PDOErrors.txt", $e->getMessage(), FILE_APPEND); }

Здесь сделана преднамеренная ошибка в выражении SELECT. Это вызовет исключение. Исключение отправит описание ошибки в log файл и выдаст сообщение пользователю.

Вставка и обновление данных

Вставка новых данных или обновление существующих - одна из наиболее часто используемых общих операций баз данных. При использовании PDO, она раскладывается на два этапа. Все, что описана в данной главе применимо и к обоим операциям UPDATE и INSERT .


Здесь приведен пример наиболее используемого типа вставки данных:

# STH - это "дескриптор состояния" $STH = $DBH->prepare("INSERT INTO folks (first_name) values ("Cathy")"); $STH->execute();

Конечно, вы можете выполнить данную операцию с использованием метода exec() , при этом количество вызовов будет меньше на один. Но лучше использовать более длинный метод для получения преимуществ подготовленных выражений. Даже если Вы собираетесь использовть их один единственный раз, подготовленные выражения помогут Вам защититься от атак на Вашу систему.

Подготовленные выражения

Подготовленные выражения - это предварительно скомпилированные выражения SQL, которые могут быть выполнены много раз с помощью пересылки только данных на сервер. Они имеют дополнительное преимущество при автоматическом заполнении шаблона данными в виде защиты от атаки посредством вложений SQL кода.

Вы можете использовать подготовленные выражения с помощью включения шаблонов в ваш код SQL. Ниже приводятся 3 примера: один без шаблонов, один с неименованными шаблонами, один с именоваными шаблонами.

# нет шаблонов - открыто для атак путем внедрения SQL кода! $STH = $DBH->("INSERT INTO folks (name, addr, city) values ($name, $addr, $city)"); # неименованые шаблоны $STH = $DBH->("INSERT INTO folks (name, addr, city) values (?, ?, ?); # именованые шаблоны $STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)");

Вам следует избегать использования первого метода. Выбор именованных или неименованных шаблонов влияет на то, как Вы устанавливаете данные для этих выражений.

Неименованные шаблоны

# назначение переменных каждому шаблону, индексируются от 1 до 3 $STH->bindParam(1, $name); $STH->bindParam(2, $addr); $STH->bindParam(3, $city); # Вставляем одну строку $name = "Дима" $addr = "ул. Лизюкова"; $city = "Москва"; $STH->execute(); # Вставляем другую строку $name = "Сеня" $addr = "Коммунистический тупик"; $city = "Питер"; $STH->execute();

Операция проходит в два этапа. На первом этапе шаблонам назначаются переменные. Затем, пременным присваиваются значения и выполняем выражение. Чтобы послать следующую порцию данных, нужно изменить значения переменных и выполнить выражение снова.

Выглядит несколько громоздко для выражений с большим количеством параметров? Конечно. Однако, если Ваши данные храняться в массиве, то все будет очень коротко:

# Данные, которые надо вставить $data = array("Моня", "проспект Незабудок", "Закутайск"); $STH = $DBH->("INSERT INTO folks (name, addr, city) values (?, ?, ?)"); $STH->execute($data);

Данные в массиве подставляются в шаблоны в порядке следования. $data идет в первый шаблон, $data - во второй, и так далее. Однако, если массив проиндексирован в другом порядке, то такая операция будет выполняться некорректно. Вам нужно следить за соответствием порядка следования шаблонов и порядком расположения данных в массиве.

Именованные шаблоны

Вот пример использования именованного шаблона:

# Первый аргумент функции - имя именованного шаблона # Именованный шаблон всегда начинается с двоеточия $STH->bindParam(":name", $name);

Вы можете использовать сокращения, но они работают с ассоциированными массивами. Пример:

# Данные, которые надо вставить $data = array("name" => "Мишель", "addr" => "переулок Кузнечный", "city" => "Cnjkbwf"); # Сокращение $STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)"); $STH->execute($data);

Ключи Вашей таблицы не нуждаются в двоеточии, но тем не менее должны соответствовать именам шаблонов. Если Вы используете массив массивов, то можно проходить по нему и просто вызывать execute для каждого массива данных.

Другая приятная особенность именованых шаблонов - способность вставлять объекты прямо в вашу базу данных, при совпадении свойств и имен полей. Пример:

# Простой объект 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("Катя","проспект Ленина","Можайск"); # Выполняем: $STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)"); $STH->execute((array)$cathy);

Преобразование типа объекта к array в execute приводит к обработке свойств как ключей массива.

Получение данных


Для получения данных используется метод идентификатора состояния ->fetch() . Перед вызовом метода fetch() нужно указать PDO как Вы будете доставать данные из базы. Можно выбрать следующие опции:

  • PDO::FETCH_ASSOC : возвращает массив, индексированный по именам столбцов
  • PDO::FETCH_BOTH (default) : возвращает массив, индексированный по именам столбцов и по номерам
  • 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 . Пример выборки данных:

$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->address = preg_replace("//", "x", $this->address); $this->other_data = $other; } }

Как только данные извлечены в класс, все символы a-z в нижнем регистре в адресе будут заменены символом 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’ Вы увидите ’Лхххххххх хх-х 5’. Конечно, существуют ситуации, когда Вы хотите, чтобы конструктор был вызван перед тем, как будут назначены данные. 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"));

Если Вам нужно передать различные данные в конструктор для каждого объекта, Вы можете устанавливать режим извлечения данных внутри метода fetch :

$i = 0; while($rowObj = $STH->fetch(PDO::FETCH_CLASS, "secret_person", array($i))) { // do stuff $i++ }

Некоторые другие полезные методы

Так как в короткой статье нельзя описать PDO полностью, то представим несколько полезных методов для выполнения базовых операций.

$DBH->lastInsertId();

Метод ->lastInsertId() всегда вызывается дескриптором базы данных (а не дескриптором состояния) и возвращает значение автоматически увеличивающегося идентификатора последней вставленной строки для данного соединения.

$DBH->exec("DELETE FROM folks WHERE 1"); $DBH->exec("SET time_zone = "-8:00"");

Метод ->exec() используется для различных вспомогательных операций.

$safe = $DBH->quote($unsafe);

Метод ->quote() квотирует строки, так что они могут быть использованы в запросах. Это Ваш резерв на случай, если подготовленные выражения не используются.

$rows_affected = $STH->rowCount();

Метод ->rowCount() возвращает значение integer , указывающее количество строк, которые обрабатываются операцией. В последней версии PDO, в соответствии с отчетом об ошибках(http://bugs.php.net/40822) данный метод не работает с выражениями SELECT . Если у Вас возникли проблемы и Вы не можете обновить PHP, получить количество строк можно следующим способом:

$sql = "SELECT COUNT(*) FROM folks"; if ($STH = $DBH->query($sql)) { # Проверка количества строк if ($STH->fetchColumn() > 0) { # Здесь должен быть код SELECT } else { echo "Нет строк соответствующих запросу."; } }

Надеюсь, что урок Вам понравился!

Для начала создадим базу данных для этого руководства:

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 () - сведения для подключения к базе, представленные в виде строки. Синтаксис описания отличается в зависимости от используемой СУБД. В примере работаем с MySQL/MariaDB, поэтому указываем:

  • имя хоста, где расположена СУБД;
  • порт (необязательно, если используется стандартный порт 3306);
  • имя базы данных;
  • кодировку (необязательно).

Строка DSN в этом случае выглядит следующим образом:

$dsn = "mysql:host=localhost;port=3306;dbname=solar_system;charset=utf8";

Первым указывается database prefix . В примере - mysql . Префикс отделяется от остальной части строки двоеточием, а каждый следующий параметр - точкой с запятой.

Создание PDO-объекта

Теперь, когда строка DSN готова, создадим PDO-объект. Конструктор на входе принимает следующие параметры:

  1. Строку DSN.
  2. Имя пользователя, имеющего доступ к базе данных.
  3. Пароль этого пользователя.
  4. Массив с дополнительными параметрами (необязательно).
$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 ( => 1 => 1 => earth => earth => blue => blue)

PDO::FETCH_ASSOC

Результат сохраняется в ассоциативном массиве, в котором ключ - имя столбца, а значение - соответствующее значение строки:

$stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_ASSOC);

В результате получим:

Array ( => 1 => earth => blue)

PDO::FETCH_NUM

При использовании этого режима результат представляется в виде массива, индексированного номерами столбцов (начиная с 0):

Array ( => 1 => earth => blue)

PDO::FETCH_COLUMN

Этот вариант полезен, если нужно получить перечень значений одного поля в виде одномерного массива, нумерация которого начинается с 0. Например:

$stmt = $pdo->query("SELECT name FROM planets");

В результате получим:

Array ( => earth => mars => jupiter)

PDO::FETCH_KEY_PAIR

Используем этот вариант, если нужно получить перечень значений двух полей в виде ассоциативного массива. Ключи массива - это данные первого столбца выборки, значения массива - данные второго столбца. Например:

$stmt = $pdo->query("SELECT name, color FROM planets"); $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);

В результате получим:

Array ( => blue => red => strange)

PDO::FETCH_OBJECT

При использовании PDO::FETCH_OBJECT для каждой извлеченной строки создаётся анонимный объект. Его общедоступные (public) свойства - имена столбцов выборки, а результаты запроса используются в качестве их значений:

$stmt = $pdo->query("SELECT name, color FROM planets"); $results = $stmt->fetch(PDO::FETCH_OBJ);

В результате получим:

StdClass Object ( => earth => 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 ( => earth => 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), которые полезны для защиты приложения от : метод 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

Представим необычный пример. Пользователю требуется выбрать список планет, причём каждый раз при выполнении запроса текущие данные удаляются из базы, а потом вставляются новые. Если после удаления произойдёт ошибка, то следующий пользователь получит пустой список. Чтобы этого избежать, используем транзакции:

$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-инъекциям;
  • поддерживаются транзакции, которые помогают сохранить целостность данных и согласованность запросов при параллельной работе пользователей.

Александр Наливайко , переводчик

Как Яндекс использует ваши данные и машинное обучение для персонализации сервисов - .

Похожие публикации