Подключение к базе данных через PDO
Для того, чтобы подключиться к нашей СУБД, как и в случае с использованием встроенных функций mysql_, нам необходим минимальный набор параметров: , адрес сервера для подключения, имя пользователя СУБД, пароль этого пользователя и имя базы данных, к которой мы будем подключаться.
Для удобства использования сохраним эти параметры в массив. О том, что такое классы и объекты в PHP вы должны иметь хоть какое-то представление. Иначе даже этот материал может быть не очень вам понятен.
Те, кому лень читать, могут сразу же скачать файл с сервера.
<?php
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
?>
Теперь пора подключаться к СУБД. На странице документации PHP, относящейся к этой тематике, есть вся исчерпывающая информаия с примерами.
Там сказано, что подключаться с помощью PDO к СУБД MySQL нужно так:
<?php
// Подключение к СУБД MySQL
$dbh = new PDO( 'mysql:host=localhost;dbname=test', $user, $pass' );
?>
Если у вас не MySQL-сервер, а например, MS SQL, используйте соответствующий драйвер, указав его имя в массиве параметров подключения.
Теперь изменим эту строку так, чтобы подключение происходило с нашими параметрами.
<?php
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
// Подключение к СУБД MySQL
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
$dbh = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] );
?>
Если все параметры указаны верно, подключение должно быть успешно установлено. Как проверить? Легко!
print "<pre>";
var_dump( $dbh );
print "</pre>";
Вы должны увидеть нечто следующее.
object(PDO)#1 (0) { }
Объект PDO создан. Всё хорошо. А давайте теперь намеренно укажем неверный пароль, просто добавив к нему нижнее подчеркивание.
<?php
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
// Подключение к СУБД MySQL
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
$dbh_bad = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] . '_' );
?>
Если у вас настроено отображение ошибок, класс PDO вам тут же покажет нечто такое:
Fatal error: Uncaught PDOException: SQLSTATE[HY000] [1045] Access denied for user 'user'@'localhost' (using password: YES) in /var/www/site.ru/document.php:30 Stack trace: #0 /var/www/site.ru/document.php(30): PDO->__construct('mysql:host=loca...', 'user', 'password_') #1 {main} thrown in /var/www/site.ru/document.php on line 30
Весьма информативно. Но для обработки ошибок лучше использовать свои функции и объекты. Пусть они наследуют свойства и методы встроенных в PHP, но будут свои. Сделаем так.
<?php
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
// Подключение к СУБД MySQL
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
$dbh_bad = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] . '_' );
}
catch( PDOException $e ) {
}
?>
Мы намеренно указали неверные параметры для подключения, и это должно было вызвать фатальную ошибку, что привело бы к остановке всего сценария, вызвав исключение PDOException. Но зачастую останавливать работу всего сценария не хочется. Хочется просто увидеть сообщение об ошибке. Для этого и используется конструкция try — catch. Можете проверить, разместив любой PHP-код под конструкцией try — catch. Он будет работать.
Теперь давайте заглянем внутрь переменной $e из блока catch.
<?php
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
// Подключение к СУБД MySQL
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
$dbh_bad = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] . '_' );
}
catch( PDOException $e ) {
print "<pre>";
var_dump( $e );
print "</pre>";
}
?>
Вы должны увидеть нечто похожее на это:
object(PDOException)#3 (8) {
["message":protected]=>
string(86) "SQLSTATE[HY000] [1045] Access denied for user 'user'@'localhost' (using password: YES)"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(1045)
["file":protected]=>
string(48) "/var/www/site.ru/document.php"
["line":protected]=>
int(123)
["trace":"Exception":private]=>
array(1) {
[0]=>
array(6) {
["file"]=>
string(48) "/var/www/site.ru/document.php"
["line"]=>
int(123)
["function"]=>
string(11) "__construct"
["class"]=>
string(3) "PDO"
["type"]=>
string(2) "->"
["args"]=>
array(3) {
[0]=>
string(41) "mysql:host=localhost;dbname=dbname"
[1]=>
string(4) "user"
[2]=>
string(10) "password_"
}
}
}
["previous":"Exception":private]=>
NULL
["errorInfo"]=>
NULL
}
Этот объект класса PDOException имеет очень много полезной информации. Давайте изменим наш код, чтобы выводить собственноручное сообщение об ошибке СУБД.
<?php
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
// Подключение к СУБД MySQL
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
$dbh_bad = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] . '_' );
}
catch( PDOException $e ) {
$error = "<hr /><p style=\"background-color:red;\">Ошибка выполнения подключения к СУБД: <strong>" . $e->getCode() . "</strong>. Ошибка: <strong>" . $e->getMessage() . "</strong></p><hr />";
print $error;
}
?>
Внутри переменной $e у нас объект исключения PDOException. Сообщение об ошибке можно получить с помощью его метода (так в объектах называются функции) getMessage(), а код ошибки — getCode().
Мы просто вывели на экран сообщение об ошибке, не прерывая работу сценария. Но это не очень правильно при попытке подключения к СУБД. Какой вообще смысл в дальнейшей работе всего сценария, если нет подключения к базе данных? Конкретно в этом случае логичнее вместо функции print() использовать функцию exit().
Значит давайте соответствующим образом изменим наш код. И сразу же сделаем так, чтобы мы не занимались каждый раз при обращении к базе данных формированием строки с сообщением об ошибке. А значит что мы будем делать для этого? Правильно! Мы объявим соответствующую функцию. Да не простую, а анонимную. Да не просто объявим, а присвоим её переменой!
<?php
/**
* Анонимной функции будет передан в качестве аргумента
* объект типа PDOException
*/
$PDOFatalError = function( PDOException $e ) {
$error = "<hr /><p style=\"background-color:red;\">Ошибка выполнения подключения к СУБД: <strong>" . $e->getCode() . "</strong>. Ошибка: <strong>" . $e->getMessage() . "</strong></p><hr />";
exit( $error );
};
// Массив значений параметров для подключения к СУБД
$aParams = array(
'driver' => 'mysql', // Здесь указываем нужный нам драйвер
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
$dbh_bad = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] . '_' );
}
catch( PDOException $e ) {
$PDOFatalError( $e );
}
?>
В общем-то, всё. Давайте заглянем внутрь подключенной базы данных, конечно же, подключившись к ней с корректными параметрами.
<?php
/**
* Анонимной функции будет передан в качестве аргумента
* объект типа PDOException
*/
$PDOFatalError = function( PDOException $e ) {
$error = "<hr /><p style=\"background-color:red;\">Ошибка выполнения подключения к СУБД: <strong>" . $e->getCode() . "</strong>. Ошибка: <strong>" . $e->getMessage() . "</strong></p><hr />";
exit( $error );
};
$aParams = array(
'driver' => 'mysql',
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname'
);
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
$dbh = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ] );
}
catch( PDOException $e ) {
$PDOFatalError( $e );
}
// Напишем строку SQL-запроса к базе данных
$sql = "SHOW TABLES";
// Обратимся к базе данных, сразу же обрабатывая результат запроса.
// Чтобы не сильно нагружать сценарий для такого простого примера,
// введем ограничение на 10 записей максимум
$i = 0;
foreach( $dbh->query( $sql ) as $row ) {
print "<pre>";
print_r( $row );
print "</pre>";
if ( $i === 10 ) { break; }
++$i;
}
?>
Многие из вас посмотрят на результат запроса с некоторым недоумением, и будут правы. Чтобы результат выглядел более привычно для многих из вас, его нужно представить в виде ассоциативного массива, например. А чтобы это произошло, при подключении к СУБД нужно указать опцию, которая сообщит объекту PDO о том, что мы все результаты запросов хотим видеть, например, как ассоциативные массивы (ну, вроде как результат запроса через функцию mysql_fetch_assoc()). Дополним массив с параметрами подключения.
Помимо озвученного, мы добавим параметр, который позволит использовать постоянные соединения с СУБД.
Постоянные соединения не закрываются при завершении работы скрипта, они кешируются и используются повторно, когда другой скрипт запрашивает соединение с теми же учетными данными. Постоянные соединения позволяют избежать создания новых подключений каждый раз, когда требуется обмен данными с базой, что в результате дает прирост скорости работы таких приложений.
Так говорится на странице официальной документации.
<?php
/**
* Анонимной функции будет передан в качестве аргумента
* объект типа PDOException
*/
$PDOFatalError = function( PDOException $e ) {
$error = "<hr /><p style=\"background-color:red;\">Ошибка выполнения подключения к СУБД: <strong>" . $e->getCode() . "</strong>. Ошибка: <strong>" . $e->getMessage() . "</strong></p><hr />";
exit( $error );
};
$aParams = array(
'driver' => 'mysql',
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname',
'charset' => 'utf8',
'params' => array( // Дополнительные параметры подключения
PDO::ATTR_PERSISTENT => true, // Использовать постоянные подключения
)
);
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
// Последним аргументом передаётся массив с дополнительными параметрами
$dbh = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ], $aParams[ 'params' ] );
// Устанавливаем режим обработки ошибок,
// используя для этого метод setAttribute() объекта PDO
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Устанавливаем режим выборки по умолчанию для объекта запроса,
// используя для этого метод setAttribute() объекта PDO
$dbh->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC );
}
catch( PDOException $e ) {
$PDOFatalError( $e );
}
// Напишем строку SQL-запроса к базе данных
$sql = "SHOW TABLES";
// Обратимся к базе данных, сразу же обрабатывая результат запроса.
// Чтобы не сильно нагружать сценарий для такого простого примера,
// введем ограничение на 10 записей максимум
$i = 0;
foreach( $dbh->query( $sql ) as $row ) {
print "<pre>";
print_r( $row );
print "</pre>";
if ( $i === 10 ) { break; }
++$i;
}
?>
При желании можно получать результат запроса в виде объекта, где имена столбцов таблицы будут являться именами свойств объекта. Для этого в месте установки режима выборки по умолчанию нужно вместо константы PDO::FETCH_ASSOC указать константу PDO::FETCH_OBJ.
Кто-то из вас спросит, и будет, в общем-то, прав: «а почему мы обработчик ошибок сделали свой, создав анонимную функцию, а вот подключаемся к базе напрямую из сценария?». Да! Давайте и это изменим! Но функцию-обработчик ошибок сделаем обычной.
<?php
/**
* Функции будет передан в качестве аргумента
* объект типа PDOException
*/
function PDOFatalError( PDOException $e ) {
$error = "<hr /><p style=\"background-color:red;\">Ошибка выполнения подключения к СУБД: <strong>" . $e->getCode() . "</strong>. Ошибка: <strong>" . $e->getMessage() . "</strong></p><hr />";
exit( $error );
}
/**
* Анонимная функция для установки соединения с СУБД
* Функции через массив можно дополнительно передавать свои параметры
*/
$Dbh = function( array $array = array() ) {
$aParams = array(
'driver' => 'mysql',
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname',
'charset' => 'utf8',
'params' => array( // Дополнительные параметры подключения
PDO::ATTR_PERSISTENT => true, // Использовать постоянные подключения
)
);
// Можно и переопределить некоторые из параметров при вызове функции
$aParams = $array += $aParams;
// Строка с параметрами подключения к СУБД
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
// Последним аргументом передаётся массив с дополнительными параметрами
$dbh = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ], $aParams[ 'params' ] );
// Устанавливаем режим обработки ошибок,
// используя для этого метод setAttribute() объекта PDO
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Устанавливаем режим выборки по умолчанию для объекта запроса,
// используя для этого метод setAttribute() объекта PDO
$dbh->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC );
}
catch( PDOException $e ) {
PDOFatalError( $e );
}
return $dbh;
};
// Напишем строку SQL-запроса к базе данных
$sql = "SHOW TABLES";
// Создаем подключение к СУБД
$dbh = $Dbh();
// Обратимся к базе данных, сразу же обрабатывая результат запроса.
// Чтобы не сильно нагружать сценарий для такого простого примера,
// введем ограничение на 10 записей максимум
$i = 0;
foreach( $dbh->query( $sql ) as $row ) {
print "<pre>";
print_r( $row );
print "</pre>";
if ( $i === 10 ) { break; }
++$i;
}
?>
А теперь смотрите! Передаем функции при установлении соединения с СУБД неверный пароль.
<?php
/**
* Функции будет передан в качестве аргумента
* объект типа PDOException
*/
function PDOFatalError( PDOException $e ) {
$error = "<hr /><p style=\"background-color:red;\">Ошибка выполнения подключения к СУБД: <strong>" . $e->getCode() . "</strong>. Ошибка: <strong>" . $e->getMessage() . "</strong></p><hr />";
exit( $error );
}
/**
* Анонимная функция для установки соединения с СУБД
* Функции через массив можно дополнительно передавать свои параметры
*/
$Dbh = function( array $array = array() ) {
$aParams = array(
'driver' => 'mysql',
'host' => 'yourhost',
'user' => 'dbusername',
'password' => 'dbuserpassword',
'dbname' => 'dbname',
'charset' => 'utf8',
'params' => array( // Дополнительные параметры подключения
PDO::ATTR_PERSISTENT => true, // Использовать постоянные подключения
)
);
// Можно и переопределить некоторые из параметров при вызове функции
$aParams = $array += $aParams;
// Строка с параметрами подключения к СУБД
$str = $aParams[ 'driver' ] . ':host='. $aParams[ 'host' ] . ';dbname=' .$aParams[ 'dbname' ] . ';charset=' . $aParams[ 'charset' ];
// Специальная конструкция для «отлавливания» ошибок
try {
// Последним аргументом передаётся массив с дополнительными параметрами
$dbh = new PDO( $str, $aParams[ 'user' ], $aParams[ 'password' ], $aParams[ 'params' ] );
// Устанавливаем режим обработки ошибок,
// используя для этого метод setAttribute() объекта PDO
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Устанавливаем режим выборки по умолчанию для объекта запроса,
// используя для этого метод setAttribute() объекта PDO
$dbh->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC );
}
catch( PDOException $e ) {
PDOFatalError( $e );
}
return $dbh;
};
// Напишем строку SQL-запроса к базе данных
$sql = "SHOW TABLES";
// Создаем подключение к СУБД
$dbh = $Dbh( array( 'password' => 'wrong_password' ) );
// Обратимся к базе данных, сразу же обрабатывая результат запроса.
// Чтобы не сильно нагружать сценарий для такого простого примера,
// введем ограничение на 10 записей максимум
$i = 0;
foreach( $dbh->query( $sql ) as $row ) {
print "<pre>";
print_r( $row );
print "</pre>";
if ( $i === 10 ) { break; }
++$i;
}
?>
На экране видим сообщение об ошибке.
Пользуйтесь! Конечно же, правильнее было бы поместить подключение к СУБД не внутрь анонимной функции, а внутрь своего объекта класса — наследника PDO. Но это уже совсем другая история...
P.S. Конечно же, этот код будет работать не на всех версиях PHP. Какая версия вам нужна? Оставлю вам это в качестве домашнего задания =)
Описанный скрипт доступен для скачивания по ссылке.
- Я опубликовал эту статью:28.05.2019
- 6 198