Мы уже несколько раз освещали PHP PDO API в Nettuts +, но, как правило, эти статьи больше фокусировались на теории, а не на приложении. Эта статья исправит это!
Проще говоря, если вы все еще используете старый PHP-интерфейс mysql для подключения к своим базам данных, читайте дальше!
Ищете ярлык?
Если вы работаете с PHP или MySQL и нуждаетесь в быстром исправлении ошибки в вашем коде, вы можете быстро и по доступной цене исправить любую отдельную ошибку разработчиком PHP Araneux в Envato Studio.
Какая?
Вполне возможно, что на данный момент единственная мысль в вашем уме: «Какого черта PDO?» Ну, это один из трех доступных API PHP для подключения к базе данных MySQL. «Три», говорите вы? Да; многие люди этого не знают, но есть три разных API для подключения:
-
mysql - MySQL улучшенный MySQL
-
pdo— объекты данных PHP
Традиционный mysql API, безусловно, выполняет свою работу и стал настолько популярным во многом благодаря тому, что он делает процесс извлечения некоторых записей из базы данных максимально простым. Например:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
/*
* Anti-Pattern
*/
# Connect
mysql_connect(‘localhost’, ‘username’, ‘password’) or die(‘Could not connect: ‘ . mysql_error());
# Choose a database
mysql_select_db(‘someDatabase’) or die(‘Could not select database’);
# Perform database query
$query = «SELECT * from someTable»;
$result = mysql_query($query) or die(‘Query failed: ‘ . mysql_error());
# Filter through rows and echo desired information
while ($row = mysql_fetch_object($result)) {
echo $row->name;
}
|
Да, приведенный выше код довольно прост, но он имеет значительную долю недостатков.
- Устаревший: Хотя он не был официально объявлен устаревшим — из-за широкого использования — с точки зрения наилучшей практики и образования, он также может быть.
- Выход из ситуации: процесс избежания пользовательского ввода оставлен на усмотрение разработчика, многие из которых не понимают или не знают, как очистить данные.
- Гибкость: API не является гибким; приведенный выше код специально создан для работы с базой данных MySQL. Что делать, если вы переключаетесь?
PDO, или PHP Data Objects, предоставляет более мощный API, который не заботится о используемом вами драйвере; это не зависит от базы данных. Кроме того, он предлагает возможность использовать подготовленные операторы, практически исключая любые опасения по поводу внедрения SQL. Ознакомьтесь с ассортиментом сценариев PDO и приложений на Envato Market, чтобы получить представление о том, что возможно.
Как?
Когда я впервые узнал об API PDO, я должен признать, что это немного пугало. Это было не потому, что API был слишком сложным (это не так) — просто старый API myqsl был настолько чертовски прост в использовании!
Не волнуйтесь, хотя; следуйте этим простым шагам, и вы сразу же приступите к работе.
Connect
Итак, вы уже знаете традиционный способ подключения к базе данных MySQL:
|
1
2
|
# Connect
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), за которым следуют необходимые данные (строка подключения) для подключения к нему.
Что хорошо в этом подходе, так это то, что если мы вместо этого хотим использовать базу данных sqlite, мы просто соответствующим образом обновляем DSN или «Имя источника данных»; мы не зависим от MySQL, как при использовании функций, таких как mysql_connect .
ошибки
Но что, если есть ошибка, и мы не можем подключиться к базе данных? Что ж, давайте обернем все в блок try/catch :
|
1
2
3
4
5
6
|
try {
$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo ‘ERROR: ‘ .
}
|
Так-то лучше! Обратите внимание, что по умолчанию режим ошибок по умолчанию для PDO — PDO::ERRMODE_SILENT . Если оставить этот параметр без изменений, вам нужно будет вручную получать ошибки после выполнения запроса.
|
1
2
|
echo $conn->errorCode();
echo $conn->errorInfo();
|
Вместо этого лучшим вариантом при разработке является обновление этого параметра до PDO::ERRMODE_EXCEPTION , который будет PDO::ERRMODE_EXCEPTION исключения по мере их возникновения. Таким образом, любые необработанные исключения будут останавливать сценарий.
Для справки доступны следующие варианты:
-
PDO::ERRMODE_SILENT -
PDO::ERRMODE_WARNING -
PDO::ERRMODE_EXCEPTION
получать
На данный момент мы создали соединение с базой данных; давайте возьмем некоторую информацию из этого. Есть два основных способа решения этой задачи: query и execute . Мы рассмотрим оба.
запрос
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
/*
* The Query Method
* Anti-Pattern
*/
$name = ‘Joe’;
try {
$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$data = $conn->query(‘SELECT * FROM myTable WHERE name = ‘ . $conn->quote($name));
foreach($data as $row) {
print_r($row);
}
} catch(PDOException $e) {
echo ‘ERROR: ‘ .
}
|
Хотя это работает, обратите внимание, что мы по-прежнему вручную PDO::quote данные пользователя с помощью метода PDO::quote . Думайте об этом методе как о более или менее эквивалентном PDO для использования mysql_real_escape_string ; он будет экранировать и указывать строку, которую вы передаете ему. В ситуациях, когда вы связываете предоставленные пользователем данные с запросом SQL, настоятельно рекомендуется вместо этого использовать подготовленные операторы. Тем не менее, если ваши SQL-запросы не зависят от данных формы, метод query является полезным выбором и делает процесс циклического просмотра результатов таким же простым, как оператор foreach .
Подготовленные заявления
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
/*
* The Prepared Statements Method
* Best Practice
*/
$id = 5;
try {
$conn = new PDO(‘mysql:host=localhost;dbname=myDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare(‘SELECT * FROM myTable WHERE id = :id’);
$stmt->execute(array(‘id’ => $id));
while($row = $stmt->fetch()) {
print_r($row);
}
} catch(PDOException $e) {
echo ‘ERROR: ‘ .
}
|
В этом примере мы используем метод prepare чтобы буквально подготовить запрос до того, как данные пользователя будут прикреплены. С этой техникой внедрение SQL практически невозможно, потому что данные никогда не вставляются в сам запрос SQL. Обратите внимание, что вместо этого мы используем именованные параметры ( :id ) для указания заполнителей.
В качестве альтернативы вы могли бы использовать
?параметры, однако, это делает для менее читаемого опыта. Придерживайтесь названных параметров.
Затем мы выполняем запрос, передавая массив, который содержит данные, которые должны быть связаны с этими заполнителями.
|
1
|
$stmt->execute(array(‘id’ => $id));
|
Альтернативный, но вполне приемлемый подход заключается в использовании метода bindParam , например, так:
|
1
2
|
$stmt->bindParam(‘:id’, $id, PDO::PARAM_INT);
$stmt->execute();
|
Указание Ouput
После вызова метода execute существует множество различных способов получения данных: массив (по умолчанию), объект и т. Д. В приведенном выше примере используется ответ по умолчанию: PDO::FETCH_ASSOC ; это может быть легко изменено, хотя, если необходимо:
|
1
2
3
|
while($row = $stmt->fetch(PDO::FETCH_OBJ)) {
print_r($row);
}
|
Теперь мы указали, что хотим взаимодействовать с набором результатов более объектно-ориентированным способом. Доступные варианты включают, но не ограничиваются:
- PDO :: FETCH_ASSOC: возвращает массив.
- PDO :: FETCH_BOTH: возвращает массив, проиндексированный как по столбцу, так и по 0.
- PDO :: FETCH_BOUND: возвращает TRUE и присваивает значения столбцов в вашем наборе результатов переменным PHP, к которым они были привязаны.
- PDO :: FETCH_CLASS: возвращает новый экземпляр указанного класса.
- PDO :: FETCH_OBJ: Возвращает анонимный объект с именами свойств, которые соответствуют столбцам.
Одна из проблем с приведенным выше кодом заключается в том, что мы не предоставляем никаких отзывов, если результаты не возвращаются. Давайте исправим это:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$stmt->execute(array(‘id’ => $id));
# Get array containing all of the result rows
$result = $stmt->fetchAll();
# If one or more rows were returned…
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
echo «No rows returned.»;
}
|
На данный момент наш полный код должен выглядеть так:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
$id = 5;
try {
$conn = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$stmt = $conn->prepare(‘SELECT * FROM myTable WHERE id = :id’);
$stmt->execute(array(‘id’ => $id));
$result = $stmt->fetchAll();
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
echo «No rows returned.»;
}
} catch(PDOException $e) {
echo ‘ERROR: ‘ .
}
|
Несколько казней
Расширение PDO становится особенно мощным при многократном выполнении одного и того же SQL-запроса, но с разными параметрами.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
try {
$conn = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
# Prepare the query ONCE
$stmt = $conn->prepare(‘INSERT INTO someTable VALUES(:name)’);
$stmt->bindParam(‘:name’, $name);
# First insertion
$name = ‘Keith’;
$stmt->execute();
# Second insertion
$name = ‘Steven’;
$stmt->execute();
} catch(PDOException $e) {
echo $e->getMessage();
}
|
После того, как запрос подготовлен, он может быть выполнен несколько раз с различными параметрами. Приведенный выше код вставит в базу данных две строки: одну с именем «Кевин», а другую — «Стивен».
CRUD
Теперь, когда у вас есть базовый процесс, давайте быстро рассмотрим различные задачи CRUD. Как вы обнаружите, необходимый код для каждого из них практически идентичен.
Создать (вставить)
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
try {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(‘INSERT INTO someTable VALUES(:name)’);
$stmt->execute(array(
‘:name’ => ‘Justin Bieber’
));
# Affected Rows?
echo $stmt->rowCount();
} catch(PDOException $e) {
echo ‘Error: ‘ .
|
Обновить
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
$id = 5;
$name = «Joe the Plumber»;
try {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(‘UPDATE someTable SET name = :name WHERE id = :id’);
$stmt->execute(array(
‘:id’ => $id,
‘:name’ => $name
));
echo $stmt->rowCount();
} catch(PDOException $e) {
echo ‘Error: ‘ .
}
|
удалять
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
$id = 5;
try {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare(‘DELETE FROM someTable WHERE id = :id’);
$stmt->bindParam(‘:id’, $id);
$stmt->execute();
echo $stmt->rowCount();
} catch(PDOException $e) {
echo ‘Error: ‘ .
}
|
Отображение объектов
Один из самых приятных аспектов PDO (в том числе mysqli) — это то, что он дает нам возможность сопоставить результаты запроса с экземпляром класса или объектом. Вот пример:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class User {
public $first_name;
public $last_name;
public function full_name()
{
return $this->first_name .
}
}
try {
$pdo = new PDO(‘mysql:host=localhost;dbname=someDatabase’, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result = $pdo->query(‘SELECT * FROM someTable’);
# Map results to object
$result->setFetchMode(PDO::FETCH_CLASS, ‘User’);
while($user = $result->fetch()) {
# Call our custom full_name method
echo $user->full_name();
}
} catch(PDOException $e) {
echo ‘Error: ‘ .
}
|
Заключительные мысли
Итог: если вы все еще используете этот старый mysql API для подключения к вашим базам данных, остановитесь. Хотя это еще не устарело, с точки зрения образования и документации , оно также может быть. Ваш код будет значительно более безопасным и оптимизированным, если вы примете расширение PDO. Проверьте позиции PDO на Envato Market, чтобы увидеть, что вы можете сделать.