PHP предлагает три разных API для подключения к MySQL. Это mysql
(удалены с PHP 7) mysqli
и PDO
расширения.
Эти mysql_*
функции используются очень популярны, но их использование не рекомендуется больше. Команда разработчиков документации обсуждает ситуацию с безопасностью базы данных, и частью этого является обучение пользователей отходить от обычно используемого расширения ext / mysql (проверьте php.internals: устарел ext / mysql ).
И более поздняя команда разработчиков PHP приняла решение генерировать E_DEPRECATED
ошибки, когда пользователи подключаются к MySQL, будь то через mysql_connect()
, mysql_pconnect()
или встроенные функции неявного подключения ext/mysql
.
ext/mysql
был официально устаревшим PHP 5.5 и был удален в PHP 7 .
Видишь красную коробку?
Когда вы заходите на mysql_*
страницу руководства по любым функциям, вы видите красную рамку, объясняющую, что она больше не должна использоваться.
Почему
Отойдя от ext/mysql
не только безопасности, но и доступа ко всем функциям базы данных MySQL.
ext/mysql
был построен для MySQL 3.23 и с тех пор получил лишь очень мало дополнений, сохраняя при этом совместимость с этой старой версией, что делает код немного сложнее поддерживать. К отсутствующим функциям, которые не поддерживаются, ext/mysql
относятся: ( из руководства по PHP ).
Причина не использовать mysql_*
функцию :
- Не в активном развитии
- Удалено с PHP 7
- Отсутствует интерфейс OO
- Не поддерживает неблокирующие, асинхронные запросы
- Не поддерживает подготовленные операторы или параметризованные запросы
- Не поддерживает хранимые процедуры
- Не поддерживает несколько утверждений
- Не поддерживает транзакции
- Не поддерживает все функции в MySQL 5.1
Выше цитата из ответа Квентина
Отсутствие поддержки подготовленных операторов особенно важно, поскольку они предоставляют более понятный и менее подверженный ошибкам метод экранирования и цитирования внешних данных, чем ручной экранирование с помощью отдельного вызова функции.
Смотрите сравнение расширений SQL .
Подавление предупреждений об устаревании
Пока код преобразуется в MySQLi
/ PDO
, E_DEPRECATED
ошибки можно подавить, установив error_reporting
в php.ini исключениеE_DEPRECATED:
error_reporting = E_ALL ^ E_DEPRECATED
Обратите внимание, что это также скрывает другие предупреждения об устаревании , которые, однако, могут относиться к вещам, отличным от MySQL. ( из руководства по PHP )
Статья PDO против MySQLi: что использовать? от Деяна Марьянович поможет вам выбрать.
И лучший способ PDO
, и я сейчас пишу простой PDO
учебник.
Простой и краткий учебник по PDO
В. Первый вопрос, который у меня возник, был: что такое «PDO»?
A. « PDO - PHP Data Objects - это уровень доступа к базе данных, обеспечивающий единый метод доступа к нескольким базам данных».
Подключение к 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
. A 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 он получается с помощью ->fetch()
метода вашего дескриптора оператора. Перед вызовом fetch лучшим подходом будет сообщить PDO, как вы хотите получать данные. В следующем разделе я объясняю это.
Режимы выборки
Обратите внимание на использование PDO::FETCH_ASSOC
в fetch()
и fetchAll()
код выше. Это говорит PDO
о необходимости возвращать строки в виде ассоциативного массива с именами полей в качестве ключей. Есть также много других режимов извлечения, которые я объясню один за другим.
Прежде всего, я объясню, как выбрать режим выборки:
$stmt->fetch(PDO::FETCH_ASSOC)
В вышеупомянутом, я использовал fetch()
. Вы также можете использовать:
Теперь я пришел к режиму выборки:
PDO::FETCH_ASSOC
: возвращает массив, проиндексированный по имени столбца, как возвращено в вашем наборе результатов
PDO::FETCH_BOTH
(по умолчанию): возвращает массив, проиндексированный как по имени столбца, так и по номеру столбца с 0 индексами, как возвращено в вашем наборе результатов
Есть еще больше вариантов! Читайте о них все в PDOStatement
документации Fetch. ,
Получение количества строк :
Вместо того 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 и верните количество затронутых строк.
Вставка и удаление будут рассмотрены позже.
Вышеуказанный метод полезен только тогда, когда вы не используете переменную в запросе. Но когда вам нужно использовать переменную в запросе, никогда не пытайтесь делать то же самое, что и выше, и там готовый оператор или параметризованный оператор .
Подготовленные заявления
В. Что такое подготовленное утверждение и зачем оно мне?
A. Подготовленный оператор - это предварительно скомпилированный оператор SQL, который можно выполнить несколько раз, отправив только данные на сервер.
Типичный рабочий процесс использования подготовленного оператора выглядит следующим образом ( цитируется из Википедии три 3 пункта ):
Подготовка : шаблон выписки создается приложением и отправляется в систему управления базами данных (СУБД). Некоторые значения остаются неопределенными, они называются параметрами, заполнителями или переменными связывания (помечены ?
ниже):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
СУБД анализирует, компилирует и выполняет оптимизацию запросов по шаблону оператора и сохраняет результат, не выполняя его.
- Выполнить : позднее приложение предоставляет (или связывает) значения для параметров, и СУБД выполняет инструкцию (возможно, возвращая результат). Приложение может выполнить инструкцию столько раз, сколько захочет, с разными значениями. В этом примере он может предоставить «Хлеб» для первого параметра и
1.00
для второго параметра.
Вы можете использовать подготовленный оператор, включив заполнители в ваш SQL. В основном есть три без заполнителей (не пытайтесь сделать это с переменной выше одной), один с неназванными заполнителями и один с именованными заполнителями.
Q. Так что теперь, как называются заполнители и как их использовать?
А. Именованные заполнители. Используйте описательные имена, начинающиеся с двоеточия, вместо вопросительных знаков. Нас не волнует позиция / порядок значений в названии местозаполнителя:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
Вы также можете связать, используя массив execute:
<?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);
В. Итак, что же такое безымянные заполнители и как их использовать?
А. Давайте приведем пример:
<?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 /*"));