Как правило, запросы в базу формируются динамически на основе данных, которые поступают извне. Например, создание пользователя чаще всего происходит во время регистрации. В таком случае код вставки выглядит так:
$name = 'Tota';
$phone = '1234';
$sql = "INSERT INTO users (username, phone) VALUES ('{$name}', '{$phone}')";
$conn->exec($sql);
Составленные таким образом запросы несут в себе серьезную опасность — SQL-инъекции. Вместо обычных данных злоумышленник может отправить текст, который изменит SQL-запрос. Представьте, что внутри переменной $phone
хранилась бы такая строка:
$phone = "'); INSERT INTO users (username, phone) VALUES ('i am hacker', '777777); --";
В таком случае после подстановки мы бы получили следующий запрос:
INSERT INTO users (username, phone) VALUES ('somename', '');
INSERT INTO users (username, phone) VALUES ('i am hacker', '777777');
Фактически, вместо одного запроса получилось два. Таким образом, злоумышленник может выполнить произвольный запрос в базу данных — то есть удалить, добавить или изменить все, что ему хочется.
В разработке существует правило «Никогда не доверяй пользовательским данным» — оно особенно важно в работе с базами данных. Любые данные перед вставкой нужно экранировать с помощью подготовленных запросов:
$sql = "INSERT INTO users (username, phone) VALUES (:name, :phone)";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':phone', $phone);
$name = 'Tota';
$phone = '12345';
$stmt->execute();
Подготовленные запросы создаются с помощью плейсхолдеров. Это заполнители, которые расставляются в местах, где ожидается подстановка данных. При этом плейсхолдеры не нужно оборачивать в кавычки, если это строки, драйвер сделает это автоматически.
Плейсхолдеры могут быть не только именованные, но и позиционные, в виде знака вопроса ?
$sql = "INSERT INTO users (username, phone) VALUES (?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $phone);
$name = 'Tota';
$phone = '12345';
$stmt->execute();
Заполнение данными выполняется с помощью метода bindParam()
. Метод принимает на вход два обязательных параметра:
- Идентификатор параметра. Для подготавливаемых запросов с именованными параметрами это будет имя в виде
:name
. Если используются позиционные параметры, то это будет позиция плейсхолдера в запросе, начиная с 1 - Данные, которые нужно подставить
Ускорение запросов
Кроме того, подготовленные запросы могут работать быстрее обычных. Дело в том, что мы можем выполнять множество запросов подряд в рамках одного подготовленного запроса. При использовании подготовленного запроса СУБД анализирует и оптимизирует запрос только один раз, а приложение запускает на выполнение уже подготовленный шаблон. За счет этого ускоряется работа
$sql = "INSERT INTO users (username, phone) VALUES (:name, :phone)";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':phone', $phone);
$name = 'Tota';
$phone = '12345';
$stmt->execute();
// Выполняем уже подготовленный запрос с другими данными
$name = 'Anna';
$phone = '9876';
$stmt->execute();
Возврат идентификатора
Когда мы вставляем данные в базу, иногда нам нужно получить идентификатор вставленной записи и потом использовать его в коде. Например, когда мы создаем какую-то сущность и хотим потом ее использовать:
$user = new User();
// Сохраняем пользователя в базу данных
// После этого становится доступен id
$user->getId();
// Его можно использовать для формирования ссылок или вставки связанных записей
Чтобы выполнить эту задачу, используем метод lastInsertId()
, который возвращает идентификатор последней вставленной записи в виде строки
$sql = "INSERT INTO users (username, phone) VALUES (:name, :phone)";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':phone', $phone);
$name = 'Tota';
$phone = '12345';
$stmt->execute();
$id = $conn->lastInsertId(); // 1
Самостоятельная работа
- Выполните шаги из урока у себя на компьютере
- Добавьте в базу данных несколько пользователей, используя один подготовленный запрос
- Используя подготовленный запрос, удалите одного из созданных пользователей по его имени
Дополнительные материалы
![Аватары экспертов Хекслета](https://cdn2.hexlet.io/assets/faces/mentors-756ab67c814cd4c22715666812a66c9dac9ac2d20176a10b0e932ee851a918e0.png)
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.