Доступ к базам данных через PHP PDO
Познакомившись с основами баз данных и языком SQL, можно приступать к экспериментам с обращением к базе данных из PHP.
На текущий момент самым правильным способом для обращения к базам данных из PHP является интерфейс PDO, что расшифровывается как "PHP Data Objects". PDO — это объектно-ориентированный универсальный интерфейс для доступа к различным СУБД. Разные СУБД, такие, как MySQL и SQLite, предоставляют свои драйверы PDO, которые позволяют PHP-скрипту обращаться к разным базам данных без изменения самого скрипта.
Давайте посмотрим, как осуществляется работа с базой данных MySQL посредством PDO. Для начала возьмём пример таблицы, созданной ранее в ходе экспериментов с SQL.
Создадим таблицу, если она ещё не создана:
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
password VARCHAR(60)
)
К этой таблице будет обращаться наш скрипт:
<?php
// Подключаемся к базе данных, указывая параметры базы данных,
// имя пользователя и пароль.
$dataSource = 'mysql:dbname=testdb;host=localhost'; // тип СУБД, хост сервера и имя базы данных
$user = 'root';
$password = '1';
$db = new PDO($dataSource, $user, $password); // Подключаемся к базе данных
// Добавляем в таблицу users записи
$db->exec("INSERT INTO users (name, password) VALUES ('admin', '123456')");
$db->exec("INSERT INTO users (name, password) VALUES ('alex', 'alex123')");
$db->exec("INSERT INTO users (name, password) VALUES ('marfa', '409ghr')");
// Делаем выборку из базы и отображаем результат
$result = $db->query("SELECT name, password FROM users ORDER BY name");
while($row = $result->fetch()) {
?>
Name: [<?php echo $row['name'] ?>] password: [<?php echo $row['password'] ?>] <br/>
<?php
}
Скрипт, будучи запущен, отобразит такой результат:
Name: [admin] password: [123456]
Name: [alex] password: [alex123]
Name: [marfa] password: [409ghr]
В этом простом примере продемонстрировано выполнение двух типов запросов: изменения данных и выборки. Запросы изменения данных не возвращают результатов: это строки, где в таблицу добавляются записи. Запрос выборки возвращает результаты виде набора строк с указанными в запросе полями. Скрипт проходит по всем строкам результата, выводя их клиенту в виде текста.
Обработка значений
В предыдущем примере для добавления записей использовались запросы INSERT с указанными непосредственно в них строковыми значениями. А что, если значения для добавления надо получить от пользователя, а в наличии есть переменные, хранящие эти значения? В этом случае, при формировании запроса придётся воспользоваться методом PDO::quote(), чтобы преобразовать введённый пользователем текст в форму, допустимую для помещения в SQL-запрос и тем самым предотвратить так называемую «SQL-инъекцию». Простой пример:
$name = $_GET['name'];
$password = $_GET['password'];
$db->exec("INSERT INTO users (name, password) VALUES ('$name', '$password')"); // - так делать нельзя!!!
$db->exec("INSERT INTO users (name, password) VALUES (" . $db->quote($name) . ", " . $db->quote($password) . ")"); // а так - можно
В первой, красной строчке, где переменные вставлены непосредственно в запрос, возможна атака SQL-инъекции. Эта атака заключается в том, что злоумышленник намеренно помещает в вводимые значения специальные символы, например, одинарные кавычки. Этим достигается нарушение структуры SQL-запроса и получение совсем иных результатов, нежели планировал программист.
Во второй строчке, какой бы текст ни указал возможный злоумышленник, в базу попадёт именно этот текст, но сам запрос не нарушится, даже если в переменных $name или $password окажутся одинарные кавычки или другой специальный символ, который привёл бы к нарушению синтаксиса запроса. Метод PDO::quote() обработает строку таким образом, что она станет безопасна для размещения в теле запроса.
Параметры запросов
Пример выше можно сделать куда понятнее и проще, используя возможность указать параметры для SQL-запроса. Делается это путём создания подготовленного запроса со значениями, заменёнными на особые метки, а затем вызова PDOStatement::execute(), в котором указываются значения параметров, которые должны быть подставлены на место меток. В качестве меток используется знак вопроса, или двоеточие с именем параметра, в случае именованных параметров.
$stmt = $db->prepare('INSERT INTO users (name, password) VALUES (?, ?)');
$stmt->execute(array($name, $password));
или
$stmt = $db->prepare('INSERT INTO users (name, password) VALUES (:name, :password)');
$stmt->execute(array(':name' => $name, ':password' => $password));
Оба этих примера демонстрируют раздельное указание SQL-запроса и значений, которыми он оперирует. Этим достигается безопасность, а также лучшая читаемость по сравнению с подходом с использованием PDO::quote() для обработки значений при формирования запроса.
Ещё кое-что...
Вопреки распространённому мнению, PDO не является уровнем абстракции от синтаксиса конкретной СУБД. Это означает, что придётся учитывать диалект языка SQL, используемые в базе данных, к которой вы подключаетесь. Каждая СУБД имеет свои небольшие различия в синтаксисе SQL, поэтому, скорее всего, переход с одной СУБД на другую потребует некоторых изменений в теле SQL-запросов.
Возможно, кому-то пригодится класс-обёртка для работы с PDO. Он упрощает программирование доступа к базе данных, сокращая объем кода и делая код проще и читабельнее.