Все ленты — последние статьи

Доступ к базе данных в PHP

Доступ к базе данных в PHP

Практическая часть использования PDO API для работы с базой данных.

Что?

Для соединения с базой данных MySQL имеется три разных API:

  • mysql
  • mysqli — MySQL Improved (улучшенная)
  • pdo — PHP Data Objects (объекты данных PHP)

 

Традиционный API mysql хорошо выполняет свою работу и получил широкое распространение благодаря тому, что делает процесс получения записей из базы данных очень простым. Например:

01 /*
02  * Демонстрация старого способа работы с базой данных MySQL
03  */
04   
05 # Соединение
06 mysql_connect('localhost', 'username', 'password') or die('Could not connect: ' . mysql_error());
07   
08 # Выбор базы данных
09 mysql_select_db('someDatabase') or die('Не могу выбрать базу данных');
10   
11 # Выполнение запроса
12 $query = "SELECT * from someTable";
13 $result = mysql_query($query) or die('Query failed: ' . mysql_error());
14   
15 # Фильтрация строк и вывод нужной информации
16 while ($row = mysql_fetch_object($result)) {
17     echo $row->name;
18 }

Да, приведенный пример очень прост. Но у него есть существенные недостатки:

  • Устарел: хотя официально его не признают устаревшим, но для новых приложений и обучения лучше использовать другие API.
  • Очистка: процесс очистки ввода пользователя от ненужного кода ложится на плечи разработчика.
  • Гибкость: данный API не гибкий и  работает только с базой данных MySQL. А если нужно перейти на другую?

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

Как?

На первый взгляд PDO API выглядит устрашающе. Не потому что он чрезмерно сложен, а потому что mysql является очень простым! Нужно просто сделать несколько несложных действий.

Соединение

Вы уже знаете устаревший способ соединения с базой данной MySQL:

1 # Соединение
2 mysql_connect('localhost', 'username', 'password') or die('Could not connect: ' . mysql_error());

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

1 $conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);

Ничего сложного.

Преимуществом данного подхода является простота изменения базы данных. Достаточно поменять имя источника данных. Нет зависимости от MySQL как при использовании функции mysql_connect.

Ошибки

Но что если произойдет ошибка при соединении с базой данных? Обернем все в блок try/catch:

1 try {
2     $conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
3     $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
4 } catch(PDOException $e) {
5     echo 'ERROR: ' . $e->getMessage();
6 }

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

1 echo $conn->errorCode();
2 echo $conn->errorInfo();

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

Доступны следующие режимы работы с ошибками:

  • PDO::ERRMODE_SILENT
  • PDO::ERRMODE_WARNING
  • PDO::ERRMODE_EXCEPTION

Получаем данные

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

Query

01 /*
02  * Метод query
03  */
04   
05 $name = 'Joe'; # user-supplied data
06   
07 try {
08     $conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
09     $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
10   
11     $data = $conn->query('SELECT * FROM myTable WHERE name = ' . $conn->quote($name));
12   
13     foreach($data as $row) {
14         print_r($row);
15     }
16 } catch(PDOException $e) {
17     echo 'ERROR: ' . $e->getMessage();
18 }

Хотя выше приведенный пример будет работать, нам нужно вручную очищать данные пользователя с помощью метода PDO::quote. Данный метод эквивалентен функции mysql_real_escape_string (в обоих случаях строка очищается и заключается в кавычки). В случаях, когда нужно построить запрос SQL с использованием пользовательских данных, лучше применять подготовленные выражения. То есть, если запрос SQL не зависит от ввода пользователя, метод query будет отличным выбором, а результат можно обработать выражением foreach.

Execute

01 /*
02  * Метод execute
03  */
04   
05 $id = 5;
06 try {
07     $conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
08     $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);   
09   
10     $stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
11     $stmt->execute(array('id' => $id));
12   
13     while($row = $stmt->fetch()) {
14         print_r($row);
15     }
16 } catch(PDOException $e) {
17     echo 'ERROR: ' . $e->getMessage();
18 }

В данном примере используется также метод prepare для подготовки запроса, перед тем, как присоединить данные пользователя. В таком случае инъекция SQL теоретически  невозможна, так как сами данные не вставляются в текст запроса SQL. Используется именованный параметр (:id) для обозначения места.

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

1 $stmt->execute(array('id' => $id));

В качестве альтернативы можно использовать метод bindParam:

1 $stmt->bindParam(':id', $id, PDO::PARAM_INT);
2 $stmt->execute();

Обрабатываем данные

После вызова метода execute есть несколько способов получить данные: массив (по умолчанию), объект и так далее. В выше приведенном примере используется режим по умолчанию: PDO::FETCH_ASSOC. Его легко поменять, если нужно:

1 while($row = $stmt->fetch(PDO::FETCH_OBJ)) {
2     print_r($row);
3 }

Теперь результат будет в виде объекта. Доступны следующие опции:

  • PDO::FETCH_ASSOC: Возвращает массив.
  • PDO::FETCH_BOTH: Возвращает массив, проиндексированный по колонке с именем.
  • PDO::FETCH_BOUND: Возвращает TRUE и назначает значение столбцов в наборе результата связанным переменным PHP.
  • PDO::FETCH_CLASS: Возвращает новый экземпляр указанного класса.
  • PDO::FETCH_OBJ: Возвращает анонимный объект, с именами свойств, соответствующих столбцам.

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

01 $stmt->execute(array('id' => $id));
02   
03 # Получаем массив, который содержит все строки результата
04 $result = $stmt->fetchAll();
05   
06 # Если получена одна или более строк…
07 if ( count($result) ) {
08     foreach($result as $row) {
09         print_r($row);
10     }
11 } else {
12     echo "Ничего не найдено.";
13 }

Полный код будет выглядеть следующим образом:

01 $id = 5;
02 try {
03   $conn = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
04   $stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
05   $stmt->execute(array('id' => $id));
06   
07   $result = $stmt->fetchAll();
08   
09   if ( count($result) ) {
10     foreach($result as $row) {
11       print_r($row);
12     }
13   } else {
14     echo "Ничего не найдено.";
15   }
16 } catch(PDOException $e) {
17     echo 'ERROR: ' . $e->getMessage();
18 }

Многократное выполнение

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

01 try {
02   $conn = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
03   $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
04   
05   # Подготавливаем запрос один раз
06   $stmt = $conn->prepare('INSERT INTO someTable VALUES(:name)');
07   $stmt->bindParam(':name', $name);
08   
09   # Первое выполнение
10   $name = 'Keith';
11   $stmt->execute();
12   
13   # Второе выполнение
14   $name = 'Steven';
15   $stmt->execute();
16 } catch(PDOException $e) {
17   echo $e->getMessage();
18 }

Как только запрос подготовлен, его можно выполнить несколько раз с разными параметрами. Выше приведенный код вставляет две строки в базу данных: одна с именем “Kevin,” а другая — “Steven.”

Типовые операции с базой данных (CRUD)

Рассмотрим выполнение типовых операций с данными (CRUD — Create, Read, Update, Delete создание, чтение, обновление, удаление). Обратите внимание, что код для них будет очень похожим.

Создание (Вставка)

01 try {
02   $pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
03   $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
04   
05   $stmt = $pdo->prepare('INSERT INTO someTable VALUES(:name)');
06   $stmt->execute(array(
07     ':name' => 'Justin Bieber'
08   ));
09   
10   # Изменено строк?
11   echo $stmt->rowCount(); // 1
12 } catch(PDOException $e) {
13   echo 'Error: ' . $e->getMessage();

Обновление

01 $id = 5;
02 $name = "Joe the Plumber";
03   
04 try {
05   $pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
06   $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
07   
08   $stmt = $pdo->prepare('UPDATE someTable SET name = :name WHERE id = :id');
09   $stmt->execute(array(
10     ':id'   => $id,
11     ':name' => $name
12   ));
13   
14   echo $stmt->rowCount(); // 1
15 } catch(PDOException $e) {
16   echo 'Error: ' . $e->getMessage();
17 }

Удаление

01 $id = 5;
02   
03 try {
04   $pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
05   $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
06   
07   $stmt = $pdo->prepare('DELETE FROM someTable WHERE id = :id');
08   $stmt->bindParam(':id', $id); // Воспользуемся методом bindParam
09   $stmt->execute();
10   
11   echo $stmt->rowCount(); // 1
12 } catch(PDOException $e) {
13   echo 'Error: ' . $e->getMessage();
14 }

Результат в виде объекта

Одним из замечательных свойств PDO (а также и mysqli) является возможность представления результата запроса в виде экземпляра класса или объекта. Например:

01 class User {
02   public $first_name;
03   public $last_name;
04   
05   public function full_name()
06   {
07     return $this->first_name . ' ' . $this->last_name;
08   }
09 }
10   
11 try {
12   $pdo = new PDO('mysql:host=localhost;dbname=someDatabase', $username, $password);
13   $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
14   
15   $result = $pdo->query('SELECT * FROM someTable');
16   
17   # Выводим результат как объект
18   $result->setFetchMode(PDO::FETCH_CLASS, 'User');
19   
20   while($user = $result->fetch()) {
21     # Вызываем наш метод full_name
22     echo $user->full_name();
23   }
24 } catch(PDOException $e) {
25   echo 'Error: ' . $e->getMessage();
26 }

Заключение

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

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: net.tutsplus.com/tutorials/php/php-database-access-are-you-doing-it-correctly/
Перевел: Сергей Фастунов