PDO (Объекты данных PHP) является уровнем абстракции доступа к данным (интерфейс) для PHP. Это работает с большинством систем баз данных.
PDO обеспечивает уровень абстракции доступа к данным, что означает, что, независимо от которой базы данных Вы используете, Вы используете те же функции для издания данных выборки и запросов. PDO не обеспечивает абстракцию базы данных; это не переписывает SQL или эмулирует недостающие возможности. Необходимо использовать полноценный уровень абстракции при необходимости в том средстве.
Источник — https://php.net/manual/en/intro.pdo.php
PDO использует DSN для определения соединения с базой данных. Это также имеет много опций соединения, которые могут помочь Вам подстроить свой экземпляр PDO. Некоторые из этих опций стоит установить по умолчанию. Вот пример:
$dsn = "mysql:host=localhost;dbname=test;charset=utf8"; $opt = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ); $pdo = new PDO($dsn,'root','', $opt);
Давайте более тщательно изучим этот код:
$dsn
содержит драйвер базы данных (mysql), хост (localhost), имя базы данных (тест) и набор символов (utf8). Конечно, эти параметры могут быть заменены переменными также.$dsn
прибывает имя пользователя и пароль. $opt
параметр является массивом, содержит параметры конфигурации.Рекомендуется установить ERRMODE_EXCEPTION
поскольку это позволит PDO выдать исключения на ошибках; этот режим является самым надежным способом обработать ошибки PDO.
Установка ATTR_DEFAULT_FETCH_MODE
также хорошая идея. Это сохраняет Вас имеющий необходимость включать его с каждой выборкой, делая Ваш код приложения менее чрезмерно увеличенным в размерах.
Существует много плохих примеров вокруг сообщения Вам перенести каждый оператор PDO в try..catch
- таким образом я должен сделать отличное примечание:
НЕ используйте
try..catch
оператор только для обработки сообщения об ошибке. Неперехваченные исключения уже превосходны с этой целью, поскольку они будут рассматривать ошибки PDO просто тем же способом как другие ошибки PHP - так, можно определить поведение, использующее по всему сайту настройки.
Пользовательский обработчик исключений мог быть добавлен позже, но он не требуется. Для новых пользователей особенно, рекомендуется использовать необработанные исключения, поскольку они чрезвычайно информативны, полезны и безопасны.
Подробнее...
Подготовленные операторы являются одной из главных причин для использования PDO.
Путь, как это работает, объяснен здесь: Как подготовленные операторы могут защитить от атак с использованием кода на SQL? Так, здесь следует правилам использования PDO:
:name
) или регулярный заполнитель (?
).prepare()
- подготовит запрос и создаст объект оператора.bindValue()
/ bindParam()
- это - дополнительный шаг, поскольку переменные могут быть переданы непосредственно в execute()
.execute()
- на самом деле выполнит запрос.fetch*
- возвратит результат запроса в применимой форме.Некоторые эмпирические правила:
bindValue()
или bindParam()
, пойдите для первого. bindValue()
менее неоднозначно и имеет меньше побочных эффектов.Так, вот пример:
$id = 1; $stm = $pdo->prepare("SELECT name FROM table WHERE id=?"); $stm->execute(array($id)); $name = $stm->fetchColumn();
PDO имеет некоторые чрезвычайно удобные методы для возврата результата запроса в различных форматах:
fetch()
- метод выборки общего назначения, подобный mysql_fetch_array()
.fetchAll()
получить все строки без цикла с условием продолжения.fetchColumn()
получить единственную скалярную величину, не получая массив сначала.fetchAll()
очень удобная функция, когда Вы делаете себя знакомыми с разделением бизнес-логики от логики представления. Это позволяет Вам получить данные сначала и затем использовать его для отображения:
$stm = $pdo->prepare("SELECT id,name FROM news WHERE dt=curdate()"); $stm->execute(); $data = $stm->fetchAll();
Теперь у нас есть все новости в $data
массив и мы можем переместиться в часть презентации:
?> <table> <? foreach ($data as $row): ?> <tr> <td> <a href="news.php?<?=$row['id']?>"> <?=htmlspecialchars($row['name'])?> </a> </td> </tr> <? endforeach ?>
Хотя подготовленные операторы являются хорошими вещами в целом, существуют некоторые хорошие советы, приемы и ловушки для знания о. В первую очередь, нужно понять, что заполнители не могут представить произвольную часть запроса, но полный литерал данных только. Ни часть литерала, ни независимо от того, что сложным выражением или ключевым словом синтаксиса можно заменить с подготовленным оператором.
Вот некоторые типичные случаи:
Подготовьте полный литерал сначала:
$name = "%$name%"; $stm = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?"); $stm->execute(array($name)); $data = $stm->fetchAll();
Когда в режиме эмуляции (который идет по умолчанию), PDO заменяет заполнителями с фактическими данными. И с "ленивой" привязкой (использующий массив в execute()
), PDO рассматривает каждый параметр как строку. В результате подготовленное LIMIT ?,?
запрос становится LIMIT '10', '10'
который является недопустимым синтаксисом, который заставляет запрос перестать работать.
Существует два решения:
Для выключения эмуляции можно выполнить этот код (или установить в массиве options соединения):
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Или связывать эти переменные явно с типом параметрического усилителя:
$stm = $pdo->prepare('SELECT * FROM table LIMIT ?, ?'); $stm->bindParam(1, $limit_from,PDO::PARAM_INT); $stm->bindParam(2, $per_page,PDO::PARAM_INT); $stm->execute(); $data = $stm->fetchAll();
Невозможно занять место, произвольная часть запроса с помощью PDO подготовила операторы. Для таких случаев как IN()
оператор, нужно создать ряд ?
s вручную и помещенный их в запрос:
$arr = array(1,2,3); $in = str_repeat('?,', count($arr) - 1) . '?'; $sql = "SELECT * FROM table WHERE column IN ($in)"; $stm = $db->prepare($sql); $stm->execute($arr); $data = $stm->fetchAll();
PDO не имеет никакого заполнителя для идентификаторов, таких как имена базы данных или имена таблиц, таким образом, разработчик должен вручную отформатировать их. Для надлежащего форматирования идентификатора следуйте этим двум правилам:
Код был бы:
$table = "`".str_replace("`","``",$table)."`";
После такого форматирования безопасно вставить $table
переменная в запрос.
Также важно всегда проверить динамические идентификаторы по списку позволенных значений. Вот краткий пример (от того, Как я могу предотвратить Внедрение SQL в PHP?):
$orders = array("name","price","qty"); //field names $key = array_search($_GET['sort'],$orders); // see if we have such a name $orderby = $orders[$key]; //if not, first one will be set automatically. smart enuf :) $query = "SELECT * FROM `table` ORDER BY $orderby"; //value is safe
другой пример мог быть найден ниже:
(от Вставляют/обновляют функцию помощника, использующую PDO),
Обычный PDO-подготовленный оператор Запроса на вставку состоит из 2-5 килобайтов повторного кода, при этом каждое имя поля повторяется шесть - десять раз. Вместо этого нам нужна компактная функция помощника для обработки переменного количества вставленных полей. Конечно, с поверхностью управляют для этих полей, для разрешения только утвержденных полей в запрос.
Следующий код основан на предположении, что имена полей HTML-формы равны названиям поля таблицы SQL. Это также использует уникальную функцию MySQL разрешения операторов НАБОРА и для ВСТАВКИ и для Запросов на обновление:
function pdoSet($fields, &$values, $source = array()) { $set = ''; $values = array(); if (!$source) $source = &$_POST; foreach ($fields as $field) { if (isset($source[$field])) { $set.="`".str_replace("`","``",$field)."`". "=:$field, "; $values[$field] = $source[$field]; } } return substr($set, 0, -2); }
Эта функция произведет корректную последовательность для оператора SET,
`field1`=:field1,`field2`=:field2
быть вставленным в запрос и $values
массив для execute()
.
Может использоваться этот путь:
$allowed = array("name","surname","email"); // allowed fields $sql = "INSERT INTO users SET ".pdoSet($allowed,$values); $stm = $dbh->prepare($sql); $stm->execute($values);
Или, для более сложного случая:
$allowed = array("name","surname","email","password"); // allowed fields $_POST['password'] = MD5($_POST['login'].$_POST['password']); $sql = "UPDATE users SET ".pdoSet($allowed,$values)." WHERE id = :id"; $stm = $dbh->prepare($sql); $values["id"] = $_POST['id']; $stm->execute($values);