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 /*"));