PHP PDO. Краткое руководство | DAndreev.com

PHP PDO. Краткое руководство

PDO

На днях решил разобраться с php PDO. Это интересно и необходимо изучать новые технологии. Русской документации нигде не нашел, потому и решил выложить. Надеюсь эта статья будет Вам интересна.

Введение

Начнем сначала PHP Data Objects(PDO) – легкий интерфейс для доступа к базам данных в языке PHP. Он может работать с большинством баз данных, такими как MS SQL ,Firebird, MySQL , Oracle, PostgreSQL , SQLite и другими. Но тут необходимо обратить внимание, что PDO предоставляет необходимый функционал для работы с базами данных, но для каждого типа базы данных должен быть установлен свой драйвер доступа для базы данных в виде расширения PHP.

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

Основы

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

Формат вот такой:

тип_базы_данных:host=имя_хоста;db=name

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

try
{
	$db = new PDO('mysql:host=localhost;dbname=test','root','');
	$rows = $db->exec("CREATE TABLE `testing`(
	id INT PRIMARY KEY AUTO_INCREMENT,
	fname VARCHAR(20) NOT NULL DEFAULT '',
	email VARCHAR(50) NOT NULL DEFAULT '')");
}
catch(PDOException $e)
{
	die("Error: ".$e->getMessage());
}

 

Если же в SQL выражении вы допустили ошибку, в PDO есть специальные функции:

errorCode() – возвращает номер ошибки, и

errorInfo() – возвращает массив, в котором, как номер ошибки, так и текст описания

Запросы непосредственно можно делать двумя функциями:

exec() и query()

Отличие их состоит в типе возвращаемого результата, exec возвращает количество затронутых в результате выполнения запроса строк, а вторая, возвращает результат запроса в объекте PDOStatement, о нем поговорим чуть ниже.

Теперь добавим эти функции в код и сделаем пример чуть более сложным:

// в начале конфиг
define('DB_DRIVER','mysql');
define('DB_HOST','localhost');
define('DB_NAME','test');
define('DB_USER','root');
define('DB_PASS','');
 
try
{
	// соединяемся с базой данных
 
	$connect_str = DB_DRIVER . ':host='. DB_HOST . ';dbname=' . DB_NAME;
	$db = new PDO($connect_str,DB_USER,DB_PASS);
 
	// вставляем несколько строк в таблицу из прошлого примера
	$rows = $db->exec("INSERT INTO `testing` VALUES
		(null, 'Ivan', 'ivan@test.com'),
		(null, 'Petr', 'petr@test.com'),
		(null, 'Vasiliy', 'vasiliy@test.com')
	");
 
	// в случае ошибки SQL выражения выведем сообщене об ошибке
 
	$error_array = $db->errorInfo();
 
	if($db->errorCode() != 0000)
 
	echo "SQL ошибка: " . $error_array[2] . '<br />';
 
	// если запрос был выполнен успешно,
	// то выведем количество затронутых строк
 
	if($rows) echo "Количество затронутых строк: " . $rows. "<br />";
 
	// теперь выберем несколько строчек из базы
 
	$result = $db->query("SELECT * FROM `testing` LIMIT 2");
 
	// в случае ошибки SQL выражения выведем сообщене об ошибке
 
	$error_array = $db->errorInfo();
 
	if($db->errorCode() != 0000)
 
	echo "SQL ошибка: " . $error_array[2] . '<br /><br />';
 
	// теперь получаем данные из класса PDOStatement
 
	while($row = $result->fetch())
	{
		// в результате получаем ассоциативный массив
		print_r($row);
	}
}
catch(PDOException $e)
{
	die("Error: ".$e->getMessage());
}

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

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

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

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

И так для начала создадим подготовленное выражение, это делается функцией Prepare()

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

$sth1 = $db->prepare(“SELECT * FROM `testing` WHERE id=:id”);

$sth2 = $db->prepare(“SELECT * FROM `testing` WHERE id=?”);

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

Если вы определили переменные знаком вопроса, то потом, в функцию execute передайте массив значений, в той, последовательности, в которой стоят переменные.

Если же вы обозначили переменные именами, то надо будет назначить каждой переменной значение посредством функций:

bindValue() – присваивает псевдопеременной значение

bindParam() – связывает псевдопеременную с настоящей переменной, и при изменении настоящей переменной, не нужно больше вызывать никаких дополнительных функций, можно сразу execute()

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

$sth1 = $db->prepare("SELECT * FROM `testing` WHERE fname=?");
$sth1->execute(array('ivan'));
while($row = $sth1->fetch())
{
	print_r($row);
}

Обратите внимание, что после создания объектом PDO подготовленного выражения в классе PDOStatement, мы пользуемся только им, соответственно, в нем есть свои функции errorCode, errorInfo, а также результат выполнения запросов, также сразу же хранится в нем.

А теперь второй способ.

Для присваивания значения псевдо переменной, воспользуемся функцией bindValue()

 

$sth3 = $db->prepare("SELECT * FROM `testing` WHERE id=:id");
 
for($id=1; $id < 4; $id++)
{
	$sth3->bindValue(':id',$id);
	$sth3->execute();
 
	while($row = $sth3->fetch())
	{
		print_r($row);
 
	}
}

 

Этот код можно записать еще проще, связав псевдопеременную с реальной:

$sth3 = $db->prepare("SELECT * FROM `testing` WHERE id=:id");
 
$sth3->bindParam(':id',$id);
 
for( $id = 1; $id < 4; $id++)
{
	$sth3->execute();
 
	while($row = $sth3->fetch())
		print_r($row);
}

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

$sth3->bindParam(‘:id’,$id, PDO::PARAM_INT);

$sth3->bindParam(‘:id’,$id, PDO::PARAM_STR);

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

Транзакции

Транзакция – это совокупность запросов базу данных, которые должны быть обязательно выполнены все. Если какой-либо запрос не выполнен или выполнен с ошибкой, то транзакция отменяется и изменений данных в базе не происходит.

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

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

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

Чтобы выключить режим автоподтверждения, выполняем команду:

$db->beginTransaction();

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

И только после того как все запросы будут выполнены, Вы можете подтвердить транзакцию функцией

$db->commit();

или отменить транзакцию

$db->rollback();

Вот небольшой пример транзакций:

try
{
	$connect_str = DB_DRIVER . ':host='. DB_HOST . ';dbname=' . DB_NAME;
	$db = new PDO($connect_str,DB_USER,DB_PASS);
 
	$rows = $db->exec("CREATE TABLE `testing`(
		id INT PRIMARY KEY AUTO_INCREMENT,
		fname VARCHAR(20) NOT NULL DEFAULT '',
		email VARCHAR(50) NOT NULL DEFAULT '',
		money INT NOT NULL DEFAULT 0) ENGINE=InnoDB;");
 
	$rows = $db->exec("INSERT INTO `testing` VALUES
		(null, 'Ivan', 'ivan@test.com', 15000),
		(null, 'Petr', 'petr@test.com', 411000),
		(null, 'Vasiliy', 'vasiliy@test.com', 1500000)
	");
 
	// Попробуем от Ивана перевести сумму 50000
	// Петру
 
	$summ = 50000;
 
	$transaction = true;
 
	$db->beginTransaction();
 
	$sth1 = $db->query("SELECT money FROM testing WHERE fname='Ivan'");
	$sth2 = $db->query("SELECT money FROM testing WHERE fname='Petr'");
	$row1 = $sth1->fetch();
	$row2 = $sth2->fetch();
 
	if(!$row1 || !$row2) $transaction = false;
 
	$total2 = $summ + $row2['money'];
	$total1 = $row1['money'] - $summ;
 
	if($total1 < 0 || $total2 < 0) $transaction = false;
 
	$num_rows1 = $db->exec("UPDATE `testing` SET money='" . $total1 . "' WHERE fname='Ivan'");
	$num_rows2 = $db->exec("UPDATE `testing` SET money='" . $total2 . "' WHERE fname='Petr'");
 
	if($transaction)
	{
		echo "Транзакция успешно прошла";
	$db->commit();
	}
	else
	{
		echo "Транзакция не прошла";
		$db->rollback();
	}
}
catch(PDOException $e)
{
	die("Error: ".$e->getMessage());
}

Тут еще следует заметить, что не все типы таблиц поддерживают транзакции, потому в этом примере я использовал таблицу InnoDb вместо стандартной MyISAM.

В заключении хотелось бы сказать, что это еще далеко не полный мануал по PDO. Всю самую свежую и полную информацию Вы всегда можете раздобыть вот здесь: http://www.php.net/manual/en/book.pdo.php

Изучайте и создавайте.

Retweet

Похожие статьи

Комментарии

Дмитрий Андреев
15 Sep, 2011в10:43

За помощь в написании статьи и обнаружении ошибок в ней, хотелось бы Выразить благодарность Михаилу. Ссылку на свой сайт он, к сожалению, не оставил.

Stalex
24 Oct, 2011в10:27

Спасибо за статтю! Помогла разобраться с основами PHP PDO.

Александр
28 Oct, 2011в18:25

А SQLSTATE error code он из себя, что представляет, строку? И когда вы проверяете (2-ой исходник)
if($db->errorCode() != 0000)
тут не пять нолей?

Дмитрий Андреев
29 Oct, 2011в07:12

Нет, SQLSTATE error code это число, потому можно сравнивать хоть с 0 хоть с 0000. Просто PDO по-моему возвращаед код четырехразрядным, потому и сравниваю с 0000.

nolka
07 Feb, 2012в21:41

Не вводите народ в заблуждение.

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

Вы попутали подготовленные выражения с хранимыми процедурами. В тексте вы описываете именно хранимые процедуры.

Дмитрий Андреев
08 Feb, 2012в05:59

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

Vitaly
08 Mar, 2012в17:26

Дмитрий, спасибо за статью!
Прошу помочь. В моем приложении после применения INSERT необходимо получить значение автоинкрементного поля id добавленной записи (при процедурном подходе используется функция mysql_insert_id()). Как получить данное значение при использовании PDO?

seyfer
12 Apr, 2012в04:15

Спасибо.

Короткое и доходчивое описание с примерами.

Дмитрий Андреев
12 Apr, 2012в09:21

Добрый день, Виталий. тут тоже все довольно просто, у самого объекта PDO есть удобная функция “lastInsertId”, только тут есть одна особенность. Если вы создали подготовленное выражение
$sth = $db->prepare();

То, функцию эту надо вызывать из самого объекта PDO:
$last_insert_id = $db->lastInsertId();

Ильдар Зарипов
28 Jul, 2012в16:07

Спасибо! Объяснили =)

Constantin T.
10 Sep, 2012в17:37

У вас опечатка в одном из исходных кодов. “SELECT1″ вместо “SELECT”

Дмитрий Андреев
20 Sep, 2012в09:33

Поправил, спасибо.

Роман Востриков
25 Sep, 2012в11:10

а преременная $count_money там не лишняя ли?

Дмитрий Андреев
25 Sep, 2012в12:02

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

Vik
07 Nov, 2012в08:42

Спасибо огромное!
Помогли разобраться!

Sergey
18 Feb, 2013в14:22

Спасибо за доходчивый расклад. Хотел бы добавить, что уже доступна и русская версия документации php и PDO:
http://www.php.net/manual/ru/book.pdo.php

Оставить комментарий