Функции mysql в PHP для работы с БД устарели, на сегодняшний день желательно использовать mysqli или PDO (PHP Data Objects).
mysqli - эта библиотека, которая по большому счёту, не предназначена для использования напрямую в коде. Она может послужить хорошим строительным материалом для создания библиотеки более высокого уровня.
При работе с mysqli следует также помнить об обеспечении безопасности вашего приложения, в частности о защите от SQL-инъекций. В случае использования PDO (с его подготовленными запросами), такая защита идёт уже «из коробки», главное правильно применить необходимые методы.
Установка в Linux:
sudo apt update sudo apt install php7.4-mysql sudo apt-get install pdo-mysql
В php.ini добавить:
extension=pdo.so extension=pdo_mysql.so
На Windows, как правило драйвер уже установлен, нужно просто проверить включен ли он в php.ini
extension=php_pdo_mysql.dll
print_r(PDO::getAvailableDrivers());
Соединения устанавливаются автоматически при создании объекта PDO от его базового класса.
Пример #1. Простое подключение:
$db = new PDO('mysql:host=localhost;dbname=pdo', 'root', 'password');
При ошибке подключения PHP выдаст ошибку:
Fatal error: Uncaught PDOException: ...
Пример #2. Обработка ошибок подключения
try { $dbh = new PDO('mysql:host=localhost;dbname=pdo', 'root', 'password'); } catch (PDOException $e) { print "Error!: " . $e->getMessage(); die(); }
В этом примере подключения используем конструкцию try...catch.
В PDO два способа выполнения запросов:
Прямой - состоит из одного шага;Подготовленный - состоит из двух шагов.query() используется для операторов, которые не вносят изменения, например SELECT. Возвращает объект PDOStatemnt, из которого с помощью методов fetch() или fetchAll извлекаются результаты запроса. Можно его сравнить с mysql resource, который возвращала mysql_query().exec() используется для операторов INSERT, DELETE, UPDATE. Возвращает число обработанных запросом строк.Прямые запросы используются только в том случае, если в запросе отсутствуют переменные и есть уверенность, что запрос безопасен и правильно экранирован.
$stmt = $db->query("SELECT * FROM categories"); while ($row = $stmt->fetch()) { echo '<pre>'; print_r($row); }
Если же в запрос передаётся хотя бы одна переменная, то этот запрос в обязательном порядке должен выполняться только через подготовленные выражения.
Подготовленное выражение - это обычный SQL запрос, в котором вместо переменной ставится специальный маркер - плейсхолдер.
PDO поддерживает позиционные плейсхолдеры (?), для которых важен порядок передаваемых переменных, и именованные (:name), для которых порядок не важен. Примеры:
$sql = "SELECT name FROM categories WHERE id = ?"; $sql = "SELECT name FROM categories WHERE name = :name";
Чтобы выполнить такой запрос, сначала его надо подготовить с помощью метода prepare(). Он также возвращает PDO statement, но ещё без данных. Чтобы их получить, надо исполнить этот запрос, предварительно передав в него наши переменные.
Передать можно двумя способами просто выполнив метод execute() и передав ему массив с переменными:
вар.1
$stmt = $pdo->prepare("SELECT `name` FROM categories WHERE `id` = ?"); $stmt->execute([$id]);
вар.2
$stmt = $pdo->prepare("SELECT `name` FROM categories WHERE `name` = :name"); $stmt->execute(['name' => $name]);
в случае именованных плейсхолдеров в execute() должен передаваться массив, в котором ключи должны совпадать с именами плейсхолдеров.
После этого можно извлечь результаты запроса:
$id = 1; $stmt = $db->prepare("SELECT * FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $category = $stmt->fetch(PDO::FETCH_LAZY); echo '<pre>'; print_r($category);
Важно! Подготовленные запросы - основная причина использовать PDO, поскольку это единственный безопасный способ выполнения SQL запросов, в которых участвуют переменные.
Мы уже выше познакомились с методом fetch(), который служит для последовательного получения строк из БД.
У метода fetch() имеется множество праметров, которые здесь описываться не будут.
В основном же рекомендуется применять fetch() в режиме FETCH_LAZY
$id = 1; $stmt = $db->prepare("SELECT * FROM categories WHERE `id` = ?"); $stmt->execute([$id]); while ($row = $stmt->fetch(PDO::FETCH_LAZY)) { echo 'Category name: '.$row->name; }
В этом режиме не тратится лишняя память, и к тому же к колонкам можно обращаться любым из трех способов - через индекс, имя, или свойство (через ->).
Недостатком данного режима является то, что он не работает сfetchAll()
Это метод для получения значения единственной колонки. Очень удобно, если запрашиваем только одно поле - в этом случае значительно сокращается количество кода:
$id = 1; $stmt = $db->prepare("SELECT `name` FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $name = $stmt->fetchColumn(); echo 'Category name: '.$name;
$data = $db->query("SELECT * FROM categories")->fetchAll(PDO::FETCH_ASSOC); foreach ($data as $k => $v){ echo 'Category name: '.$v['name'].'<br>'; }
Работая с подготовленными запросами, следует понимать, что плейсхолдер может заменять только строку или число. Ни ключевое слово, ни идентификатор, ни часть строки или набор строк через плейсхолдер подставить нельзя.
Поэтому для LIKE необходимо сначала подготовить строку поиска целиком, а потом ее подставлять в запрос:
$search = 'комп'; $query = "SELECT * FROM categories WHERE `name` LIKE ?"; $params = ["%$search%"]; $stmt = $db->prepare($query); $stmt->execute($params); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); $i = 1; foreach ($data as $category){ echo $i++ . '. ' . $category['name'].'<br>'; }
Здесь может вознинуть проблема! Поиск может не работать, потому как из базы у вас приходят данные в неправильной кодировке. Необходимо добавить кодировку в подключение, если она там не указана:
$db = new PDO('mysql:host=localhost;dbname=pdo;charset=utf8mb4', 'root', '');
==Важно!== Когда PDO работает в режиме эмуляции, все данные, которые были переданы напрямую в execute(), форматируются как строки. То есть, эскейпятся и обрамляются кавычками. Поэтому LIMIT ?,? превращается в LIMIT '10', '10' и очевидным образом вызывает ошибку синтаксиса и, соответственно, пустой массив данных.
Решение #1: Отключить режим эмуляции:
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Решение #2: Биндить эти цифры через bindValue(), принудительно выставляя им тип PDO::PARAM_INT:
$limit = 3; $stm = $db->prepare('SELECT * FROM categories LIMIT ?'); $stm->bindValue(1, $limit, PDO::PARAM_INT); $stm->execute(); $data = $stm->fetchAll(); echo '<pre>'; print_r($data);
При выборке из таблицы необходимо доставать записи, соответствующие всем значениям массива.
$arr = [1,3,6]; $in = str_repeat('?,', count($arr) - 1) . '?'; $sql = "SELECT * FROM categories WHERE `id` IN ($in)"; $stm = $db->prepare($sql); $stm->execute($arr); $data = $stm->fetchAll(); echo '<pre>'; print_r($data);
$name = 'Новая категория'; $query = "INSERT INTO `categories` (`name`) VALUES (:name)"; $params = [ ':name' => $name ]; $stmt = $pdo->prepare($query); $stmt->execute($params);
$id = 1; $name = 'Изменённая запись'; $query = "UPDATE `categories` SET `name` = :name WHERE `id` = :id"; $params = [ ':id' => $id, ':name' => $name ]; $stmt = $pdo->prepare($query); $stmt->execute($params);
$id = 1; $query = "DELETE FROM `categories` WHERE `id` = ?"; $params = [$id]; $stmt = $pdo->prepare($query); $stmt->execute($params);