Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Подготовленные запросы

Запросам почти всегда нужны данные извне: идентификатор из адреса, имя из формы. Вставлять их в текст SQL напрямую опасно. Подготовленные запросы решают эту задачу: текст запроса и данные передаются в базу отдельно.

Примеры используют базу SQLite в памяти и здесь не запускаются — выполните их у себя.

Запрос с параметром

В тексте запроса на месте значения ставят знак вопроса. Метод prepare() готовит запрос, а execute() выполняет его с переданными значениями:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
$pdo->exec("INSERT INTO users (name, age) VALUES ('Анна', 25), ('Олег', 31)");
 
$statement = $pdo->prepare('SELECT age FROM users WHERE name = ?');
$statement->execute(['Анна']);
 
echo $statement->fetchColumn(); // 25

Кавычки вокруг знака вопроса не нужны — база сама понимает, что это строка.

Именованные параметры

Когда параметров несколько, удобнее дать им имена. В запросе имя пишется с двоеточием:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
$pdo->exec("INSERT INTO users (name, age) VALUES ('Анна', 25), ('Олег', 31)");
 
$statement = $pdo->prepare('SELECT name FROM users WHERE age > :min AND age < :max');
$statement->execute(['min' => 20, 'max' => 30]);
 
echo $statement->fetchColumn(); // Анна

Добавление записи

Так же выполняют INSERT. Идентификатор новой строки возвращает lastInsertId():

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
 
$statement = $pdo->prepare('INSERT INTO users (name, age) VALUES (:name, :age)');
$statement->execute(['name' => 'Анна', 'age' => 25]);
 
echo $pdo->lastInsertId(); // 1

Изменение и удаление

Для UPDATE и DELETE метод rowCount() сообщает, сколько строк затронуто:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
$pdo->exec("INSERT INTO users (name, age) VALUES ('Анна', 25), ('Олег', 31)");
 
$statement = $pdo->prepare('UPDATE users SET age = :age WHERE name = :name');
$statement->execute(['age' => 26, 'name' => 'Анна']);
 
echo $statement->rowCount(); // 1

Зачем это нужно

Если склеить запрос из строк, посетитель сможет изменить его смысл. Здесь вместо имени передан фрагмент SQL — и запрос возвращает всех пользователей:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
$pdo->exec("INSERT INTO users (name, age) VALUES ('Анна', 25), ('Олег', 31)");
 
$name = "' OR '1'='1";
 
$unsafe = $pdo->query("SELECT * FROM users WHERE name = '$name'")->fetchAll();
echo count($unsafe), "\n"; // 2
 
$statement = $pdo->prepare('SELECT * FROM users WHERE name = ?');
$statement->execute([$name]);
echo count($statement->fetchAll()); // 0

Такая атака называется SQL-инъекцией. В подготовленном запросе та же строка — просто странное имя, которого в таблице нет.

Частая ошибка

Параметром можно заменить только значение. Название таблицы или столбца через ? подставить нельзя. Если оно зависит от выбора пользователя — например, столбец для сортировки, — сверяйте его со списком разрешённых значений.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.