Pdo подставить параметры без выполнения запроса. Как работать с PDO? Полное руководство. Вставка в базу данных, метод exec()

Соединение с базой данных устанавливается тогда, когда создаётся экземпляр класса 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

Я думаю настало время повысить свой экспириенс, и перейти при работе с базой данных с mysql_ функций на PDO. Эта библиотека является мощным и быстрым дополнением к PHP. Одно из ее преимуществ – работа со многими базами данных (MS SQL , MySQL , PostgreSQL , Oracle и др.). Также отличительной характеристикой этой библиотеки являются подготовленные выражения, так называемые prepared statements, которые должны ускорить работу с базой, а главное сделать обмен данным безопасными и забыть о таких уязвимостях как sql-enjection. Кроме того, есть и другие очень полезные возможности. В этой статье я попытался собрать часто используемые примеры работ, по которым можно сразу понять суть работы PDO.

В PHP существуют три расширения для работы с базой MySQL: mysql, mysqli и PDO. PHP PDO (PHP Data Objects) включено в PHP 5.1 и выше. Как я понял, на сегодняшний момент, функции для работы с базой данных mysql_ не рекомендуют к использованию, так как разработка php_mysql остановилась на поддержке функционала MySQL 4.1.3. и также не поддерживает транзакции, объектный интерфейс и подвержено уязвимостям при подстановке значений в запрос. После mysql_ появилось расширение mysqli (MySQL Improved в 5 версии), которое поддерживает новые возможности MySQL и использует как ОПП синтаксис так и процедурное программирование. Все эти библиотеки используют стандартную клиентскую библиотеку MySQL (libmysql). В этой же заметке давайте на живых примерах посмотрим как осуществляется работа с mysql, при помощи самого свежего расширения – PDO .

PDO Соединение с БД

//пример соединения с MySQL при помощи PDO $db = new PDO("mysql:host=localhost;dbname=test", $user, $pass);

Как только будет установлено успешное соединение с конкретным сервером базы данных, то будет возвращен объект PDO. Этот объект позволяет выполнять самые разнообразные задачи баз данных.

Если есть какие-либо ошибки подключения, то сработает механизм исключений – PDOException. Нужно всегда оборачивать операции PDO в блок try/catch. Вы можете поймать исключение, если вы хотите обрабатывать ошибки, или вы можете оставить его для глобального обработчика исключений (exception), которые вы создали с помощью set_exception_handler (). В PDO есть специальные функции для ошибок: errorCode() – вернет номер ошибки, errorInfo() – вернет массив с номером ошибки и описанием. Они нужны так как по умолчанию режим работы с ошибками стоит ERRMODE_SILENT. В этом случае чтобы увидеть эти ошибки придется их вызвать:

Echo $conn->errorCode(); echo $conn->errorInfo();

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

Try { $db = new PDO("mysql:host=$host;dbname=$dbname", $user, $password); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec("set names utf8"); } catch(PDOException $e) { echo $e->getMessage(); }

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

//Закрытие соединения $db = null;

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

//Постоянное соединение $dbh = new PDO("mysql:host=localhost;dbname=test", $user, $pass, array(PDO::ATTR_PERSISTENT => true));

Теперь, когда вы увидели, как открывать и закрывать соединение, давайте рассмотрим другие примеры работы с PDO. В данном случае я собираюсь показать вам, как выполнять запросы к конкретной базе данных. Запросы можно делать 3 функциями: exec(), query() и prepare+execute.

Exec()

Первый – exec вернет только кол-во задействованных строк или FALSE при ошибке и используется там, где не возвращаются данные, например при удалении:

//использование метода exec() try{ $db = new PDO("mysql:host=localhost;dbname=test","user","password"); $delrows=$db->exec("DELETE FROM users WHERE id>20"); echo "Количество удаленных строк: ".$delrows; } catch(PDOException $e){ echo "Error: ".$e->getMessage(); exit(); } //Еще $db->exec("DELETE FROM folks WHERE 1"); //или $db->exec("SET time_zone = "-8:00""); //или $db->exec("CREATE TABLE `test`(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL DEFAULT "", email VARCHAR(50) NOT NULL DEFAULT "")"); //или $db->exec("SET CHARACTER SET utf8");

Query()

Второй – query() вернет результат в объекте PDOStatement. Также возвращает результат или FALSE при ошибке. Ему можно доверить простые запросы. Можно использовать query() с условием (я правда не знаю зачем кому то это может понадобиться), но тогда все равно придется экранировать данные методом PDO::quote

//Простые запросы $db->query("SET CHARACTER SET utf8"); $db->query("SELECT * FROM users"); //Можно вычислить количество строк $stmt = $db->query("SELECT * FROM table"); $row_count = $stmt->rowCount(); echo $row_count." rows selected"; //Еще вариант с количеством $stmt = $db->query("SELECT * from users"); $rows = $stmt->fetchAll(); $count = count($rows); foreach($rows as $row) { print_r($row); } //Запрос с условием и экранированием $conn->query("SELECT * FROM table WHERE id = " . $conn->quote($id));

lastInsertId() возвращает идентификатор последней вставленной строки базы данных.

//метод lastInsertId() возвращает id последней записи $db->query("INSERT INTO users SET name="Vasya",address="Here",email="[email protected]""); $insertId=$db->lastInsertId();

Подготовленные выражения – prepared statments.

Третий способ - prepare+execute - Подготовленные выражения , они же подготовленные инструкции они же плейсхолдеры они же prepared statments или связываемые переменные, позволяют определить выражение один раз, а затем многократно его выполнять с разными параметрами. Также они позволяют отделить переменные от запроса, что делает код безопаснее и повышает скорость выполнения. В вашем коде больше не надо будет пытаться очистить передаваемые данные. Мы сделаем это только один раз перед выполнением запроса к БД. Для этого используем функцию Prepare() ; В качестве параметра она принимает SQL запрос, но в нем, вместо переменных используются метки, в виде знака вопроса ‘?’ или номеров ‘:1′, или переменой, имя которой начинается с двоеточия ‘:’. Если вы остановились на знаках вопроса (:цифрах), то вам надо в функцию execute передать массив значений, в соответствующей последовательности. Если ваш выбор именованные переменные, то надо назначить каждой переменной значение через одну из двух функций: либо bindValue(), которая присваивает псевдо-переменной значение, либо bindParam(), которая связывает псевдо-переменную с настоящей переменной. Третьим параметром можно указать тип переменной, например $db->bindParam(‘:id’,$id, PDO::PARAM_INT).

//Не именованные метки try { $stmt = $db->prepare("INSERT INTO test (label,color) VALUES (?,?)"); $stmt -> execute(array("perfect","green")); } catch(PDOException $e){ echo "Error: ".$e->getMessage(); exit(); } //stmt - это "дескриптор состояния" //Именованные метки $stmt = $db->prepare("INSERT INTO test (label,color) VALUES (:label,:color)"); $stmt -> execute(array("label"=>"perfect", "color"=>"green")); //Еще вариант $stmt = $db->prepare("INSERT INTO users (firstname, lastname, email) VALUES (:firstname, :lastname, :email)"); $stmt->bindParam(":firstname", $firstname); $stmt->bindParam(":lastname", $lastname); $stmt->bindParam(":email", $email); $firstname = "John"; $lastname = "Smith"; $email = "[email protected]"; $stmt->execute();

Еще раз напомню, что если вы не используете подготовленные выражения но все-таки хотите обезопасить передаваемые данные, то это можно сделать при помощи функции PDO:quote.

PDO SELECT данных

Для выборки данных используются методы fetch() или fetchAll(). Перед вызовом функции нужно указать PDO как Вы будете доставать данные из базы. PDO::FETCH_ASSOC вернет строки в виде ассоциативного массива с именами полей в качестве ключей. PDO::FETCH_NUM вернет строки в виде числового массива. По умолчанию выборка происходит с PDO::FETCH_BOTH, который дублирует данные как с численными так и с ассоциативными ключами, поэтому рекомендуется указать один способ, чтобы не иметь дублирующих массивов:

$stmt = $db->query("SELECT * from users"); //Установка fetch mode $stmt->setFetchMode(PDO::FETCH_ASSOC); while($row = $stmt->fetch()) { echo "

" . $row["firstname"] . " " . $row["lastname"] . "

"; echo "

" . $row["email"] . "


"; }

Либо можно указать метод выборки в самом методе ->fetch()

$stmt = $db->query("SELECT * FROM table"); while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { echo $row["field1"]." ".$row["field2"]; //и т.д... } //Внимание, не работающий пример с LIKE! $stmt = $db->prepare("SELECT field FROM table WHERE field LIKE %?%"); $stmt->bindParam(1, $search, PDO::PARAM_STR); $stmt->execute(); //Нужно так: $stmt = $db->prepare("SELECT field FROM table WHERE field LIKE ?"); $stmt->bindValue(1, "%$search%", PDO::PARAM_STR); $stmt->execute(); //Еще пример $stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?"); $stmt->bindValue(1, $id, PDO::PARAM_INT); $stmt->bindValue(2, $name, PDO::PARAM_STR); $stmt->execute(); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); //Еще вариант $stmt = $db->prepare("SELECT * from users"); $stmt -> execute(); while($row = $stmt->fetch()) { print_r($row); }

PDO UPDATE данных

Происходит по существу также как и INSERT и SELECT (в данном случае опять же будем использовать именованные метки (placeholders)):

$stmt = $db->prepare("UPDATE users set email = :email where lastname=:lastname"); $stmt->bindParam(":lastname", $lastname); $stmt->bindParam(":email", $email); $lastname = "Smith"; $email = "[email protected]"; $stmt->execute();

DELETE Удаление происходит простейшим образом:

$db->exec("DELETE FROM users");

Конечно вы можете также использовать именованные параметры (placeholders) при удалении.

Если у вас есть свои часто используемые заготовки при работе с расширением PHP PDO, буду признателен если вы ими поделитесь. Ссылка на мануал

Если у вас возникла ошибка Undefined variable: DBH … то можете почитать как это исправить.

Google помог найти шпаргалку по PDO, может кому пригодится:

PDO (PHP Data Objects) - расширение PHP, которое реализует взаимодействие с базами данных при помощи объектов. Профит в том, что отсутствует привязка к конкретной системе управления базами данных. PDO поддерживает СУБД: MySQL, PostgreSQL, SQLite, Oracle, Microsoft SQL Server и другие.

Почему стоит использовать PDO

Функции mysql в PHP для работы с БД давно уже устарели, на сегодняшний день желательно использовать mysqli или PDO (PHP Data Objects). Кроме того, mysqli - эта библиотека, которая по большому счёту, не предназначена для использования напрямую в коде. Она может послужить хорошим строительным материалом для создания библиотеки более высокого уровня. При работе с mysqli следует также помнить об обеспечении безопасности вашего приложения, в частности о защите от SQL-инъекций. В случае использования PDO (с его подготовленными запросами), такая защита идёт уже "из коробки", главное правильно применить необходимые методы.

Тестовая база данных с таблицей

// Из консоли Windows mysql> CREATE DATABASE `pdo-test` CHARACTER SET utf8 COLLATE utf8_general_ci; USE pdo-test; CREATE TABLE categories (id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), name VARCHAR(255) NOT NULL); INSERT INTO `categories` (`name`) VALUES ("Ноутбуки и планшеты"), ("Компьютеры и периферия"), ("Комплектующие для ПК"), ("Смартфоны и смарт-часы"), ("Телевизоры и медиа"), ("Игры и приставки"), ("Аудиотехника"), ("Фото-видеоаппаратура"), ("Офисная техника и мебель"), ("Сетевое оборудование"), ("Крупная бытовая техника"), ("Товары для кухни"), ("Красота и здоровье"), ("Товары для дома"), ("Инструменты"), ("Автотовары");

Установка PDO

// Установка в Linux sudo apt update sudo apt install php7.2-mysql sudo apt-get install pdo-mysql // В php.ini добавить extension=pdo.so extension=pdo_mysql.so // На Windows, как правило драйвер уже установлен, нужно просто проверить включен ли он в php.ini extension=php_pdo_mysql.dll

Проверить доступные драйвера

print_r(PDO::getAvailableDrivers());

Соединение с базой данных

Соединения устанавливаются автоматически при создании объекта PDO от его базового класса.

// Пример #1. Простое подключение $db = new PDO("mysql:host=localhost;dbname=pdo", "root", "password");

При ошибке подключения PHP выдаст ошибку:

Fatal error: Uncaught PDOException: ... // Пример #2. Обработка ошибок подключения try { $dbh = new PDO("mysql:host=localhost;dbname=pdo", "root", "password"); } catch (PDOException $e) { print "Error!: " . $e->getMessage(); die(); }

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

Подготовленные и прямые запросы

В PDO два способа выполнения запросов:

  • Прямой - состоит из одного шага;
  • Подготовленный - состоит из двух шагов.

Прямые запросы

  • query() используется для операторов, которые не вносят изменения, например SELECT . Возвращает объект PDOStatemnt , из которого с помощью методов fetch() или fetchAll извлекаются результаты запроса. Можно его сравнить с mysql resource , который возвращала mysql_query() .
  • exec() используется для операторов INSERT, DELETE, UPDATE . Возвращает число обработанных запросом строк.

Прямые запросы используются только в том случае, если в запросе отсутствуют переменные и есть уверенность, что запрос безопасен и правильно экранирован.

$stmt = $db->query("SELECT * FROM categories"); while ($row = $stmt->fetch()) { echo "

";
 print_r($row);
}

Подготовленные запросы

Если же в запрос передаётся хотя бы одна переменная, то этот запрос в обязательном порядке должен выполняться только через подготовленные выражения. Что это значит? Это обычный SQL запрос, в котором вместо переменной ставится специальный маркер - плейсхолдер. PDO поддерживает позиционные плейсхолдеры (?), для которых важен порядок передаваемых переменных, и именованные (:name), для которых порядок не важен. Примеры:

$sql = "SELECT name FROM categories WHERE id = ?"; $sql = "SELECT name FROM categories WHERE name = :name";

Чтобы выполнить такой запрос, сначала его надо подготовить с помощью метода prepare() . Она также возвращает PDO statement , но ещё без данных. Чтобы их получить, надо исполнить этот запрос, предварительно передав в него наши переменные. Передать можно двумя способами: Чаще всего можно просто выполнить метод execute() , передав ему массив с переменными:

$stmt = $pdo->prepare("SELECT `name` FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $stmt = $pdo->prepare("SELECT `name` FROM categories WHERE `name` = :name"); $stmt->execute(["name" => $name]);

Как видно, в случае именованных плейсхолдеров в execute() должен передаваться массив, в котором ключи должны совпадать с именами плейсхолдеров. После этого можно извлечь результаты запроса:

$id = 1; $stmt = $db->prepare("SELECT * FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $category = $stmt->fetch(PDO::FETCH_LAZY); echo "

";
print_r($category);

ВАЖНО! Подготовленные запросы - основная причина использовать PDO, поскольку это единственный безопасный способ выполнения SQL запросов, в которых участвуют переменные.

Получение данных. Метод fetch()

Мы уже выше познакомились с методом fetch() , который служит для последовательного получения строк из БД. Этот метод является аналогом функции mysq_fetch_array() и ей подобных, но действует по-другому: вместо множества функций здесь используется одна, но ее поведение задается переданным параметром. В подробностях об этих параметрах будет написано в , а в качестве краткой рекомендации посоветую применять fetch() в режиме FETCH_LAZY

$id = 1; $stmt = $db->prepare("SELECT * FROM categories WHERE `id` = ?"); $stmt->execute([$id]); while ($row = $stmt->fetch(PDO::FETCH_LAZY)) { echo "Category name: ".$row->name; }

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

Получение данных. Метод fetchColumn()

Также у PDO statement есть метод для получения значения единственной колонки. Очень удобно, если мы запрашиваем только одно поле - в этом случае значительно сокращается количество кода:

$id = 1; $stmt = $db->prepare("SELECT `name` FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $name = $stmt->fetchColumn(); echo "Category name: ".$name;

Получение данных. Метод fetchAll()

$data = $db->query("SELECT * FROM categories")->fetchAll(PDO::FETCH_ASSOC); foreach ($data as $k => $v){ echo "Category name: ".$v["name"]."
"; }

PDO и оператор LIKE

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

$search = "комп"; $query = "SELECT * FROM categories WHERE `name` LIKE ?"; $params = ["%$search%"]; $stmt = $db->prepare($query); $stmt->execute($params); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); $i = 1; foreach ($data as $category){ echo $i++ . ". " . $category["name"]."
"; }

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

$db = new PDO("mysql:host=localhost;dbname=pdo;charset=utf8", "root", "");

PDO и оператор LIMIT

Важно! Когда PDO работает в режиме эмуляции, все данные, которые были переданы напрямую в execute() , форматируются как строки. То есть, эскейпятся и обрамляются кавычками. Поэтому LIMIT ?,? превращается в LIMIT "10", "10" и очевидным образом вызывает ошибку синтаксиса и, соответственно, пустой массив данных.

Решение #1: Отключить режим эмуляции:

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Решение #2: Биндить эти цифры через bindValue() , принудительно выставляя им тип PDO::PARAM_INT:

$limit = 3; $stm = $db->prepare("SELECT * FROM categories LIMIT ?"); $stm->bindValue(1, $limit, PDO::PARAM_INT); $stm->execute(); $data = $stm->fetchAll(); echo "

";
print_r($data);

PDO и оператор IN

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

$arr = ; $in = str_repeat("?,", count($arr) - 1) . "?"; $sql = "SELECT * FROM categories WHERE `id` IN ($in)"; $stm = $db->prepare($sql); $stm->execute($arr); $data = $stm->fetchAll(); echo "

";
print_r($data);

Добавление записей

$name = "Новая категория"; $query = "INSERT INTO `categories` (`name`) VALUES (:name)"; $params = [ ":name" => $name ]; $stmt = $pdo->prepare($query); $stmt->execute($params);

Изменение записей

$id = 1; $name = "Изменённая запись"; $query = "UPDATE `categories` SET `name` = :name WHERE `id` = :id"; $params = [ ":id" => $id, ":name" => $name ]; $stmt = $pdo->prepare($query); $stmt->execute($params);

Удаление записей

$id = 1; $query = "DELETE FROM `categories` WHERE `id` = ?"; $params = [$id]; $stmt = $pdo->prepare($query); $stmt->execute($params);

Использование транзакций

try { // Начало транзакции $pdo->beginTransaction(); // ... code // Если в результате выполнения нашего кода всё прошло успешно, // то зафиксируем этот результат $pdo->commit(); } catch (Exception $e) { // Иначе, откатим транзакцию. $pdo->rollBack(); echo "Ошибка: " . $e->getMessage(); }

Важно! Транзакции в PDO работают только с таблицами InnoDB

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

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

Введение в 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 "Нет строк соответствующих запросу."; } }

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

  • Перевод

Множество PHP-разработчиков привыкли использовать для работы с базами данных расширения mysql и mysqli. Но с версии 5.1 в PHP существует более удобный способ - PHP Data Objects . Этот класс, сокращенно именуемый PDO, предоставляет методы для работы с объектами и prepared statements , которые заметно повысят вашу продуктивность!

Введение в PDO

«PDO – PHP Data Objects – это прослойка, которая предлагает универсальный способ работы с несколькими базами данных.»

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


Эта статья написана для людей, которые пользуются mysql и mysqli, чтобы помочь им в переходе на более мощный и гибкий PDO.

Поддержка СУБД

Это расширение может поддерживать любую систему управления базами данных, для которой существует PDO-драйвер. На момент написания статьи доступны следующие драйвера:
  • PDO_CUBRID (CUBRID)
  • PDO_DBLIB (FreeTDS / Microsoft SQL Server / Sybase)
  • PDO_FIREBIRD (Firebird/Interbase 6)
  • PDO_IBM (IBM DB2)
  • PDO_INFORMIX (IBM Informix Dynamic Server)
  • PDO_MYSQL (MySQL 3.x/4.x/5.x)
  • PDO_OCI (Oracle Call Interface)
  • PDO_ODBC (ODBC v3 (IBM DB2, unixODBC and win32 ODBC))
  • PDO_PGSQL (PostgreSQL)
  • PDO_SQLITE (SQLite 3 and SQLite 2)
  • PDO_SQLSRV (Microsoft SQL Server)
  • PDO_4D (4D)
Впрочем, не все из них есть на вашем сервере. Увидеть список доступных драйверов можно так:
print_r(PDO::getAvailableDrivers());

Подключение

Способы подключения к разным СУБД могут незначительно отличаться. Ниже приведены примеры подключения к наиболее популярным из них. Можно заметить, что первые три имеют идентичный синтаксис, в отличие от SQLite.
try { # MS SQL Server и Sybase через PDO_DBLIB $DBH = new PDO("mssql:host=$host;dbname=$dbname", $user, $pass); $DBH = new PDO("sybase:host=$host;dbname=$dbname", $user, $pass); # MySQL через PDO_MYSQL $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); # SQLite $DBH = new PDO("sqlite:my/database/path/database.db"); } catch(PDOException $e) { echo $e->getMessage(); }
Пожалуйста, обратите внимание на блок try/catch – всегда стоит оборачивать в него все свои PDO-операции и использовать механизм исключений (об этом чуть дальше).

$DBH расшифровывается как «database handle» и будет использоваться на протяжении всей статьи.

Закрыть любое подключение можно путем переопределения его переменной в null.
# закрывает подключение $DBH = null;
Больше информации по теме отличительных опций разных СУБД и методах подключения к ним можно найти на php.net .

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

PDO умеет выбрасывать исключения при ошибках, поэтому все должно находиться в блоке try/catch. Сразу после создания подключения, PDO можно перевести в любой из трех режимов ошибок:
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Но стоит заметить, что ошибка при попытке соединения будет всегда вызывать исключение.

PDO::ERRMODE_SILENT

Это режим по умолчанию. Примерно то же самое вы, скорее всего, используете для отлавливания ошибок в расширениях mysql и mysqli. Следующие два режима больше подходят для DRY программирования.

PDO::ERRMODE_WARNING

Этот режим вызовет стандартный Warning и позволит скрипту продолжить выполнение. Удобен при отладке.

PDO::ERRMODE_EXCEPTION

В большинстве ситуаций этот тип контроля выполнения скрипта предпочтителен. Он выбрасывает исключение, что позволяет вам ловко обрабатывать ошибки и скрывать щепетильную информацию. Как, например, тут:
# подключаемся к базе данных try { $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); # Черт! Набрал DELECT вместо SELECT! $DBH->prepare("DELECT name FROM people")->execute(); } catch(PDOException $e) { echo "Хьюстон, у нас проблемы."; file_put_contents("PDOErrors.txt", $e->getMessage(), FILE_APPEND); }
В SQL-выражении есть синтаксическая ошибка, которая вызовет исключение. Мы можем записать детали ошибки в лог-файл и человеческим языком намекнуть пользователю, что что-то случилось.

Insert и Update

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


Тривиальный пример вставки новых данных:
# STH означает "Statement Handle" $STH = $DBH->prepare("INSERT INTO folks (first_name) values ("Cathy")"); $STH->execute();
Вообще-то можно сделать то же самое одним методом exec(), но двухшаговый способ дает все преимущества prepared statements. Они помогают в защите от SQL-инъекций, поэтому имеет смысл их использовать даже при однократном запросе.

Prepared Statements

Использование prepared statements укрепляет защиту от SQL-инъекций.

Prepared statement - это заранее скомпилированное SQL-выражение, которое может быть многократно выполнено путем отправки серверу лишь различных наборов данных. Дополнительным преимуществом является невозможность провести SQL-инъекцию через данные, используемые в placeholder’ах.

Ниже находятся три примера prepared statements.
# без placeholders - дверь SQL-инъекциям открыта! $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ($name, $addr, $city)"); # безымянные placeholders $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)"); # именные placeholders $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");
Первый пример здесь лишь для сравнения, его стоит избегать. Разница между безымянными и именными placeholder’ами в том, как вы будете передавать данные в prepared statements.

Безымянные placeholder’ы

# назначаем переменные каждому placeholder, с индексами от 1 до 3 $STH->bindParam(1, $name); $STH->bindParam(2, $addr); $STH->bindParam(3, $city); # вставляем одну строку $name = "Daniel" $addr = "1 Wicked Way"; $city = "Arlington Heights"; $STH->execute(); # вставляем еще одну строку, уже с другими данными $name = "Steve" $addr = "5 Circle Drive"; $city = "Schaumburg"; $STH->execute();
Здесь два шага. На первом мы назначаем всем placeholder’ам переменные (строки 2-4). Затем назначаем этим переменным значения и выполняем запрос. Чтобы послать новый набор данных, просто измените значения переменных и выполните запрос еще раз.

Если в вашем SQL-выражении много параметров, то назначать каждому по переменной весьма неудобно. В таких случаях можно хранить данные в массиве и передавать его:
# набор данных, которые мы будем вставлять $data = array("Cathy", "9 Dark and Twisty Road", "Cardiff"); $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)"); $STH->execute($data);
$data вставится на место первого placeholder’а, $data - на место второго, и т.д. Но будьте внимательны: если ваши индексы сбиты, это работать не будет.

Именные placeholder’ы

# первым аргументом является имя placeholder’а # его принято начинать с двоеточия # хотя работает и без них $STH->bindParam(":name", $name);
Здесь тоже можно передавать массив, но он должен быть ассоциативным. В роли ключей должны выступать, как можно догадаться, имена placeholder’ов.
# данные, которые мы вставляем $data = array("name" => "Cathy", "addr" => "9 Dark and Twisty", "city" => "Cardiff"); $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)"); $STH->execute($data);
Одним из удобств использования именных placeholder’ов является возможность вставки объектов напрямую в базу данных, если названия свойств совпадают с именами параметров. Вставку данных, к примеру, вы можете выполнить так:
# класс для простенького объекта class person { public $name; public $addr; public $city; function __construct($n,$a,$c) { $this->name = $n; $this->addr = $a; $this->city = $c; } # так далее... } $cathy = new person("Cathy","9 Dark and Twisty","Cardiff"); # а тут самое интересное $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)"); $STH->execute((array)$cathy);
Преобразование объекта в массив при execute() приводит к тому, что свойства считаются ключами массива.

Выборка данных



Данные можно получить с помощью метода ->fetch(). Перед его вызовом желательно явно указать, в каком виде они вам требуются. Есть несколько вариантов:
  • PDO::FETCH_ASSOC: возвращает массив с названиями столбцов в виде ключей
  • PDO::FETCH_BOTH (по умолчанию): возвращает массив с индексами как в виде названий стобцов, так и их порядковых номеров
  • PDO::FETCH_BOUND: присваивает значения столбцов соответствующим переменным, заданным с помощью метода ->bindColumn()
  • PDO::FETCH_CLASS: присваивает значения столбцов соответствующим свойствам указанного класса. Если для какого-то столбца свойства нет, оно будет создано
  • PDO::FETCH_INTO: обновляет существующий экземпляр указанного класса
  • PDO::FETCH_LAZY: объединяет в себе PDO::FETCH_BOTH и PDO::FETCH_OBJ
  • PDO::FETCH_NUM: возвращает массив с ключами в виде порядковых номеров столбцов
  • PDO::FETCH_OBJ: возвращает анонимный объект со свойствами, соответствующими именам столбцов
На практике вам обычно хватит трех: FETCH_ASSOC, FETCH_CLASS, и FETCH_OBJ. Чтобы задать формат данных, используется следующий синтаксис:
$STH->setFetchMode(PDO::FETCH_ASSOC);
Также можно задать его напрямую при вызове метода ->fetch().

FETCH_ASSOC

При этом формате создается ассоциативный массив с названиями столбцов в виде индексов. Он должен быть знаком тем, кто использует расширения mysql/mysqli.
# поскольку это обычный запрос без placeholder’ов, # можно сразу использовать метод query() $STH = $DBH->query("SELECT name, addr, city from folks"); # устанавливаем режим выборки $STH->setFetchMode(PDO::FETCH_ASSOC); while($row = $STH->fetch()) { echo $row["name"] . "\n"; echo $row["addr"] . "\n"; echo $row["city"] . "\n"; }
Цикл while() переберет весь результат запроса.

FETCH_OBJ

Данный тип получения данных создает экземпляр класса std для каждой строки.
# создаем запрос $STH = $DBH->query("SELECT name, addr, city from folks"); # выбираем режим выборки $STH->setFetchMode(PDO::FETCH_OBJ); # выводим результат while($row = $STH->fetch()) { echo $row->name . "\n"; echo $row->addr . "\n"; echo $row->city . "\n"; }

FETCH_CLASS

При использовании fetch_class данные заносятся в экземпляры указанного класса. При этом значения назначаются свойствам объекта ДО вызова конструктора. Если свойства с именами, соответствующими названиям столбцов, не существуют, они будут созданы автоматически (с областью видимости public).

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

Для примера возьмем ситуацию, когда вам нужно скрыть часть адреса проживания человека.
class secret_person { public $name; public $addr; public $city; public $other_data; function __construct($other = "") { $this->addr = preg_replace("//", "x", $this->addr); $this->other_data = $other; } }
При создании объекта все латинские буквы в нижнем регистре должны замениться на x. Проверим:
$STH = $DBH->query("SELECT name, addr, city from folks"); $STH->setFetchMode(PDO::FETCH_CLASS, "secret_person"); while($obj = $STH->fetch()) { echo $obj->addr; }
Если в базе данных адрес выглядит как ’5 Rosebud’, то на выходе получится ’5 Rxxxxxx’.

Конечно, иногда будет требоваться, чтобы конструктор вызывался ПЕРЕД присваиванием значений. PDO такое тоже позволяет.
$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "secret_person");
Теперь, когда вы дополнили предыдущий пример дополнительной опцией (PDO::FETCH_PROPS_LATE), адрес видоизменяться не будет, так как после записи значений ничего не происходит.

Наконец, при необходимости можно передавать конструктору аргументы прямо при создании объекта:
$STH->setFetchMode(PDO::FETCH_CLASS, "secret_person", array("stuff"));
Можно даже передавать разные аргументы каждому объекту:
$i = 0; while($rowObj = $STH->fetch(PDO::FETCH_CLASS, "secret_person", array($i))) { // что-то делаем $i++; }

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

Хотя эта статья не может (и не пытается) охватить все аспекты работы с PDO (это огромный модуль!), оставить без упоминания следующие несколько функций нельзя.
$DBH->lastInsertId();
Метод ->lastInsertId() возвращает id последней вставленной записи. Стоит заметить, что он всегда вызывается у объекта базы данных (в статье он именуется $DBH), а не объекта с выражением ($STH).
$DBH->exec("DELETE FROM folks WHERE 1"); $DBH->exec("SET time_zone = "-8:00"");
Метод ->exec() используется для операций, которые не возвращают никаких данных, кроме количества затронутых ими записей.
$safe = $DBH->quote($unsafe);
Метод ->quote() ставит кавычки в строковых данных таким образом, что их становится безопасно использовать в запросах. Пригодится, если вы не используете prepared statements.
$rows_affected = $STH->rowCount();
Метод ->rowCount() возвращает количество записей, которые поучаствовали в операции. К сожалению, эта функция отказывалась работать с SELECT-запросами вплоть до PHP 5.1.6. Если обновить версию PHP не представляется возможным, количество записей можно получить так:
$sql = "SELECT COUNT(*) FROM folks"; if ($STH = $DBH->query($sql)) { # проверяем количество записей if ($STH->fetchColumn() > 0) { # делаем здесь полноценную выборку, потому что данные найдены! } else { # выводим сообщение о том, что удовлетворяющих запросу данных не найдено } }

Заключение

Надеюсь, этот материал поможет кому-то из вас осуществить миграцию с расширений mysql и mysqli.