PDO против MySQLi: что вам следует использовать? >
И лучший способ - PDO
, и сейчас я пишу простой PDO
учебник.
Простое и короткое руководство по PDO
В. Первый вопрос в моей голове был: что такое `PDO`?
А. « PDO - объекты данных PHP - это уровень доступа к базе данных, обеспечивающий единый метод доступа к нескольким базам данных».
Подключение к MySQL
С функцией mysql_*
или мы можем сказать это по-старому (устарело в PHP 5.5 и выше)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
С PDO
: Все, что вам нужно сделать, это создать новый объект PDO
. Конструктор принимает параметры для указания источника базы данных PDO
. Конструктор в основном принимает четыре параметра: DSN
(имя источника данных) и, возможно, username
, password
.
.
Здесь, я думаю, вы знакомы со всем, кроме DSN
; это новое в PDO
. DSN
- это в основном строка параметров, которые сообщают PDO
, какой драйвер использовать, и сведения о подключении. Для получения дополнительной информации см. PDO MySQL DSN.
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
Примечание: вы также можете использовать charset=UTF-8
, но иногда это вызывает ошибку, поэтому лучше использовать utf8
.
Если есть какая-либо ошибка соединения, будет выдан объект PDOException
, который можно перехватить для дальнейшей обработки Exception
.
Хорошее чтение : Подключения и управление подключениями ¶
Вы также можете передать несколько параметров драйвера в виде массива четвертому параметру. Я рекомендую передать параметр, который переводит PDO
в режим исключения. Поскольку некоторые драйверы PDO
не поддерживают собственные подготовленные операторы, PDO
выполняет эмуляцию подготовки. Он также позволяет вручную включить эту эмуляцию. Чтобы использовать собственные подготовленные операторы на стороне сервера, вы должны явно установить его false
.
Другой - отключить эмуляцию подготовки, которая включена в драйвере MySQL
по умолчанию, но эмуляция подготовки должна быть отключена для безопасного использования PDO
.
Позже я объясню, почему подготовительную эмуляцию следует отключить. Чтобы узнать причину, проверьте этот пост.
Его можно использовать только в том случае, если вы используете старую версию MySQL
, которую я не рекомендую.
Ниже приведен пример того, как это можно сделать:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password',
array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Можно ли установить атрибуты после построения PDO?
Да , мы также можем установить некоторые атрибуты после построения PDO с помощью метода setAttribute
:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Обработка ошибок
Обработка ошибок в PDO
намного проще, чем в mysql_*
.
Обычная практика использования mysql_*
:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
- не лучший способ справиться с ошибкой, поскольку мы не можем справиться с проблемой в die
. Он просто внезапно завершит скрипт, а затем отобразит ошибку на экране, который вы обычно НЕ хотите показывать своим конечным пользователям, и позволит кровавым хакерам обнаружить вашу схему. В качестве альтернативы, возвращаемые значения функций mysql_*
часто можно использовать вместе с mysql_error () для обработки ошибок.
PDO
предлагает лучшее решение: исключения. Все, что мы делаем с PDO
, должно быть заключено в блок try
- catch
. Мы можем принудительно переключить PDO
в один из трех режимов ошибки, установив атрибут режима ошибки. Ниже приведены три режима обработки ошибок.
-
PDO::ERRMODE_SILENT
. Он просто устанавливает коды ошибок и действует почти так же, как mysql_*
, где вы должны проверять каждый результат, а затем смотреть на $db->errorInfo();
, чтобы получить подробную информацию об ошибке.
-
PDO::ERRMODE_WARNING
Поднять E_WARNING
. (Предупреждения во время выполнения (нефатальные ошибки). Выполнение сценария не останавливается.)
-
PDO::ERRMODE_EXCEPTION
: выбросить исключения. Он представляет собой ошибку, вызванную PDO. Вы не должны выбрасывать PDOException
из вашего собственного кода. См. Исключения для получения дополнительной информации об исключениях в PHP. Он действует очень похоже на or die(mysql_error());
, когда его не ловят. Но в отличие от or die()
, PDOException
можно поймать и аккуратно обработать, если вы захотите это сделать.
Хорошее чтение :
Нравится:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
И вы можете обернуть его в try
- catch
, как показано ниже:
try {
//Connect as appropriate as above
$db->query('hi'); //Invalid query!
}
catch (PDOException $ex) {
echo "An Error occured!"; //User friendly message/message you want to show to user
some_logging_function($ex->getMessage());
}
Вам не нужно иметь дело с try
- catch
прямо сейчас. Вы можете поймать его в любое удобное время, но я настоятельно рекомендую использовать try
- catch
. Также имеет смысл поймать его за пределами функции, которая вызывает материал PDO
:
function data_fun($db) {
$stmt = $db->query("SELECT * FROM table");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
//Then later
try {
data_fun($db);
}
catch(PDOException $ex) {
//Here you can handle error and show message/perform action you want.
}
Кроме того, вы можете обрабатывать с помощью or die()
или, можно сказать, mysql_*
, но это будет действительно по-разному. Вы можете скрыть опасные сообщения об ошибках в производственной среде, повернув display_errors off
и просто прочитав журнал ошибок.
Теперь, после прочтения всего вышеперечисленного, вы, вероятно, думаете: какого черта, когда я просто хочу начать использовать простые утверждения SELECT
, INSERT
, UPDATE
или DELETE
? Не волнуйтесь, мы идем:
Выбор данных
Итак, в mysql_*
вы делаете следующее:
<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());
$num_rows = mysql_num_rows($result);
while($row = mysql_fetch_assoc($result)) {
echo $row['field1'];
}
Теперь в PDO
вы можете сделать это так:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
или
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
Примечание : если вы используете метод, как показано ниже (query()
), этот метод возвращает объект PDOStatement
. Поэтому, если вы хотите получить результат, используйте его, как указано выше.
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
В PDO Data он получается с помощью ->fetch()
, метода дескриптора вашего оператора. Перед вызовом fetch лучшим подходом было бы сообщить PDO, как вы хотите получать данные. В следующем разделе я объясняю это.
Режимы выборки
Обратите внимание на использование PDO::FETCH_ASSOC
в приведенных выше кодах fetch()
и fetchAll()
. Это указывает PDO
возвращать строки в виде ассоциативного массива с именами полей в качестве ключей. Также есть много других режимов выборки, которые я объясню один за другим.
Прежде всего, я объясню, как выбрать режим выборки:
$stmt->fetch(PDO::FETCH_ASSOC)
Выше я использовал fetch()
. Вы также можете использовать:
Теперь я перехожу в режим выборки:
-
PDO::FETCH_ASSOC
: возвращает массив, проиндексированный по имени столбца, как возвращено в вашем наборе результатов
-
PDO::FETCH_BOTH
(по умолчанию): возвращает массив, проиндексированный как по имени столбца, так и по номеру столбца с индексом 0, как возвращено в вашем наборе результатов
Есть еще больше вариантов! Прочтите обо всех них в PDOStatement
Получить документацию..
Получение количества строк :
Вместо использования mysql_num_rows
для получения количества возвращенных строк вы можете получить PDOStatement
и выполнить rowCount()
, например:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
Получение последнего вставленного идентификатора
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
Вставить и обновить или удалить операторы
В функции mysql_*
мы делаем следующее:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
А в pdo то же самое можно сделать с помощью:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
В приведенном выше запросе PDO::exec
выполнить инструкцию SQL и вернуть количество затронутых строк.
Вставка и удаление будут рассмотрены позже.
Вышеупомянутый метод полезен только тогда, когда вы не используете переменную в запросе. Но когда вам нужно использовать переменную в запросе, никогда не пытайтесь сделать что-то подобное, и там для подготовленный оператор или параметризованный оператор is.
Подготовленные отчеты
В. Что такое подготовленный оператор и зачем он мне нужен?
A. Подготовленный оператор - это предварительно скомпилированный оператор SQL, который может выполняться несколько раз отправив на сервер только данные.
Типичный рабочий процесс использования подготовленного оператора выглядит следующим образом (цитата из Википедии, три 3 точки):
-
Подготовить : шаблон оператора создается приложением и отправляется в систему управления базами данных (СУБД). Некоторые значения остаются неопределенными, они называются параметрами, заполнителями или связанными переменными (помечены ниже ?
):
INSERT INTO PRODUCT (name, price) VALUES (? ?)
-
СУБД анализирует, компилирует и выполняет оптимизацию запроса в шаблоне оператора и сохраняет результат, не выполняя его.
-
Выполнить : позже приложение предоставляет (или связывает) значения для параметров, и СУБД выполняет оператор (возможно, возвращая результат). Приложение может выполнять оператор сколько угодно раз с разными значениями. В этом примере он может предоставить "хлеб" для первого параметра и
1.00
для второго параметра.
Вы можете использовать подготовленный оператор, включив заполнители в свой SQL. В основном есть три без заполнителей (не пытайтесь использовать это с переменной выше), один с безымянными заполнителями и один с именованными заполнителями.
В. Итак, что такое именованные заполнители и как их использовать?
A. Именованные заполнители. Вместо вопросительных знаков используйте описательные имена, которым предшествует двоеточие. Нас не заботит позиция / порядок значений в заполнителе имени:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Вы также можете выполнить привязку, используя массив выполнения:
<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
Еще одна приятная особенность для друзей OOP
состоит в том, что именованные заполнители имеют возможность вставлять объекты непосредственно в вашу базу данных, предполагая, что свойства соответствуют именованным полям. Например:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
В. Итак, что такое безымянные заполнители и как их использовать?
A. Давайте рассмотрим пример:
<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (? ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();
и
$stmt = $db->prepare("INSERT INTO folks (name, add) values (? ?)");
$stmt->execute(array('john', '29 bla district'));
В приведенном выше примере вы можете увидеть эти ?
вместо имени, например, в заполнителе имени. Теперь в первом примере мы назначаем переменные различным заполнителям ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Затем мы присваиваем значения этим заполнителям и выполняем инструкцию. Во втором примере первый элемент массива переходит к первому ?
, а второй - ко второму ?
.
ПРИМЕЧАНИЕ : В безымянных заполнителях мы должны позаботиться о правильном порядке элементов в массиве, который мы передаем методу PDOStatement::execute()
.
SELECT
, INSERT
, UPDATE
, DELETE
подготовленные запросы
-
SELECT
:
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-
INSERT
:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
-
DELETE
:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
-
UPDATE
:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
ПРИМЕЧАНИЕ:
Однако PDO
и / или MySQLi
не полностью безопасны. Проверьте ответ Достаточно ли подготовленных операторов PDO для предотвращения внедрения SQL? от ircmaxell. Также я цитирую часть его ответа:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
Ошибка должна быть похожей на: Неустранимая ошибка: Неперехваченная ошибка: Вызов неопределенной функции mysql_connect () ...
Одно только устаревание - достаточная причина, чтобы их избегать