English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

PHP базовые курсы

PHP продвинутые курсы

PHP & MySQL

PHP справочник

Предварительные инструкции PHP MySQL

В этом руководстве вы узнаете, как использовать предварительно обработанные запросы в MySQL с помощью PHP.

Что такое предварительно обработанные запросы

Предварительно обработанные запросы (также известные как параметризованные запросы) это просто шаблон SQL-запроса, который содержит占идители вместо фактических значений параметров. При выполнении запроса эти занидители будут заменены фактическими значениями.

MySQLi поддерживает использование анонимных позиционных占идителей(?), например:

INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?);

PDO поддерживает анонимные позиционные占位ители(?)и именованные占идители. Именованные占位ители начинаются с двоеточия(:)и следуются идентификатором, например:

INSERT INTO persons (first_name, last_name, email)
VALUES (:first_name, :last_name, :email);

The execution of prepared statements includes two phases: preparation and execution.

  • Preparation - In the preparation phase, a SQL statement template will be created and sent to the database server. The server parses the statement template, performs syntax checking and query optimization, and stores it for later use.

  • Execution - Parameter values will be sent to the server during execution. The server creates a statement from the statement template and these values to execute it.

Prepared statements are very useful, especially when you use different values (for example, a series of statements) to execute a specific INSERT statement multiple times. The following section describes some of the main advantages of using them.

Advantages of using prepared statements

A prepared statement can be executed efficiently multiple times for the same statement, because it is parsed only once and can be executed multiple times. Since only placeholder values need to be transmitted to the database server during each execution, rather than the entire SQL statement, it can also reduce bandwidth usage to the maximum extent.

Prepared statements also provide strong protection againstSQL injectionThis is because parameter values are not directly embedded in the SQL query string. Parameter values are sent to the database server separately from the query using different protocols, so they do not interfere with it. After parsing the statement template, the server uses these values directly during execution. This is why prepared statements are less likely to fail and are considered one of the most critical elements of database security.

The following example will show you how prepared statements actually work:

Пример:面向过程的方式

<?php
/* Попытка подключения к MySQL серверу. Предполагается, что вы запускаете MySQL.
Сервер сdefault настройками (пользователь без пароля "root"). */
$link = mysqli_connect("localhost", "root", "", "demo");
 
// Проверка подключения
if($link === false){
    die("Ошибка: Не удалось подключиться. " . mysqli_connect_error());
}
 
//Использование предобrabатываемого предложения
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = mysqli_prepare($link, $sql)){
    // Привязка переменных в качестве параметров к подготовленному запросу
    mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
    
    /* Set parameter values and execute, this statement inserts another row again. */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "[email protected]";
    mysqli_stmt_execute($stmt);
    
    /* Set parameter values and execute the statement to insert a row. */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "[email protected]";
    mysqli_stmt_execute($stmt);
    
    echo "Запись успешна вставлена.";
}
    echo "Error: Unable to prepare query: $sql. " . mysqli_error($link);
}
 
//Закрытие предложения
mysqli_stmt_close($stmt);
 
//Закрытие соединения
mysqli_close($link);
?>

Пример:面向对象的方式

<?php
/* Попытка подключения к MySQL серверу. Предполагается, что вы запускаете MySQL.
Сервер сdefault настройками (пользователь без пароля "root"). */
$mysqli = new mysqli("localhost", "root", "", "demo");
 
// Проверка подключения
if($mysqli === false){
    die("Ошибка: Не удалось подключиться. " . $mysqli->connect_error);
}
 
// Use a prepared statement
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = $mysqli->prepare($sql)){
    // Bind variables as parameters to a prepared statement
    $stmt->bind_param("sss", $first_name, $last_name, $email);
    
    /* Set parameter values and execute. */
    Повторное выполнение этого предложения для вставки другой строки */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "[email protected]";
    $stmt->execute();
    
    /* Установка значений параметров и выполнение
        Запрос на вставку строки
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "Record inserted successfully.";
}
    echo "Error: Unable to prepare query: $sql. " . $mysqli->error;
}
 
//Закрытие предложения
$stmt->close();
 
//Закрытие соединения
$mysqli->close();
?>

Пример: метод PDO

<?php
/* Попытка подключения к MySQL серверу. Предполагается, что вы запускаете MySQL.
Сервер сdefault настройками (пользователь без пароля "root"). */
try{
    $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", "");
    // Set PDO error mode to exception
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
    die("Ошибка: Не удалось подключиться. " . $e->getMessage());
}
 
//Попытка выполнения вставки запроса
try{
    //Использование предобrabатываемого предложения
    $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)";
    $stmt = $pdo->prepare($sql);
    
    // Bind parameters to the statement
    $stmt->bindParam(':first_name', $first_name, PDO::PARAM_STR);
    $stmt->bindParam(':last_name', $last_name, PDO::PARAM_STR);
    $stmt->bindParam(':email', $email, PDO::PARAM_STR);
    
    /* Установка значений параметров и выполнение,
      Повторное выполнение этого предложения для вставки другой строки */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "[email protected]";
    $stmt->execute();
    
    /* Установка значений параметров и выполнение
        Запрос на вставку строки
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "Запись успешна вставлена.";
} catch(PDOException $e){
    die("Ошибка: не удалось подготовить/выполнить запрос: $sql. " . $e->getMessage());
}
 
// Закрытие запроса
unset($stmt);
 
//Закрытие соединения
unset($pdo);
?>

Как вы видите в примере выше, мы подготовили только одно предложение INSERT, но его выполняли多次, передавая разные наборы параметров.

использование кода (стиль программы)

В примере SQL INSERT-запроса, вопросительный знак используется какfirst_name,last_nameиemailЗамачник для значения поля

Функция mysqli_stmt_bind_param() привязывает переменные к占ачникам (?) в шаблоне SQL-запроса. Замачники (?) будут заменены на фактические значения переменных, сохраненные при выполнении. В качестве второго параметра предоставляется строка типа данных, например, строка "sss" определяет тип данных каждого привязанного переменной как string (строка).

Строка типа данных определяет тип данных соответствующего привязанного переменной, параметры имеют следующие четыре типа:

  • i - integer (целое число)

  • d - double (двоеточечная плавающая точка)

  • s - string (строка)

  • b - BLOB (binary large object: двоичный большой объект)

Количество типов данных и символов в строке типа данных должно соответствовать количеству占位ников в шаблоне SQL-запроса.

Использование ввода, полученного через веб-форму

Если вы помните из предыдущей главы, мы создали HTML-форму дляВставка данных в базу данныхЗдесь мы расширяем этот пример, выполняя подготовленный запрос. Вы можете использовать тот же HTML-формуляр для тестирования следующего примера вставки скрипта, но просто убедитесь, что в свойстве action формы указан правильный имя файла.

Это обновленный PHP-код для вставки данных. Если внимательно посмотреть на пример, вы заметите, что мы не используем mysqli_real_escape_string(), как в примере из предыдущей главы. Поскольку в подготовленных запросах пользовательский ввод никогда не заменяется на строку запроса напрямую, нет необходимости правильно их экранировать.

Пример:面向过程的方式

<?php
/* Попытка подключения к MySQL серверу. Предполагается, что вы запускаете MySQL.
Сервер сdefault настройками (пользователь без пароля "root"). */
$link = mysqli_connect("localhost", "root", "", "demo");
 
// Проверка подключения
if($link === false){
    die("Ошибка: Не удалось подключиться. " . mysqli_connect_error());
}
 
//Использование предобrabатываемого предложения
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = mysqli_prepare($link, $sql)){
    // Привязка переменных к подготовленному запросу как параметрам
    mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
    
    // Установка параметров
    $first_name = $_REQUEST['first_name'];
    $last_name = $_REQUEST['last_name'];
    $email = $_REQUEST['email'];
    
    // Попытка выполнения подготовленного запроса
    if(mysqli_stmt_execute($stmt)){
        echo "Запись успешна вставлена.";
    }
        echo "Ошибка: Не удалось выполнить запрос: $sql. " . mysqli_error($link);
    }
}
    echo "Ошибка: Не удалось выполнить запрос: $sql. " . mysqli_error($link);
}
 
// Закрытие запроса
mysqli_stmt_close($stmt);
 
//Закрытие соединения
mysqli_close($link);
?>

Пример:面向对象的方式

<?php
/* Попытка подключения к MySQL серверу. Предполагается, что вы запускаете MySQL.
Сервер сdefault настройками (пользователь без пароля "root"). */
$mysqli = new mysqli("localhost", "root", "", "demo");
 
// Проверка подключения
if($mysqli === false){
    die("Ошибка: Не удалось подключиться. " . $mysqli->connect_error);
}
 
//Использование предобrabатываемого предложения
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = $mysqli->prepare($sql)){
    // Привязка переменных в качестве параметров к подготовленному запросу
    $stmt->bind_param("sss", $first_name, $last_name, $email);
    
    // Установка параметров
    $first_name = $_REQUEST['first_name'];
    $last_name = $_REQUEST['last_name'];
    $email = $_REQUEST['email'];
    
    // Попытка выполнения подготовленного запроса
    if($stmt->execute()){
        echo "Запись успешна вставлена.";
    }
        echo "Ошибка: Не удалось выполнить запрос: $sql. " . $mysqli->error;
    }
}
    echo "Ошибка: Не удалось выполнить запрос: $sql. " . $mysqli->error;
}
 
//Закрытие предложения
$stmt->close();
 
//Закрытие соединения
$mysqli->close();
?>

Пример: метод PDO

<?php
/* Попытка подключения к MySQL серверу. Предполагается, что вы запускаете MySQL.
Сервер сdefault настройками (пользователь без пароля "root"). */
try{
    $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", "");
    //Установка режима ошибок PDO на исключения
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
    die("Ошибка: Не удалось подключиться. " . $e->getMessage());
}
 
//Попытка выполнения вставки запроса
try{
    //Использование предобrabатываемого предложения
    $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)";
    $stmt = $pdo->prepare($sql);
    
    //Привязка параметров к предложению
    $stmt->bindParam(':first_name', $_REQUEST['first_name'], PDO::PARAM_STR);
    $stmt->bindParam(':last_name', $_REQUEST['last_name'], PDO::PARAM_STR);
    $stmt->bindParam(':email', $_REQUEST['email'], PDO::PARAM_STR);
    
    //Выполнение предобrabатываемого предложения
    $stmt->execute();
    echo "Запись успешна вставлена.";
} catch(PDOException $e){
    die("Ошибка: Не удалось подготовить/выполнить запрос $sql. " . $e->getMessage());
}
 
//Закрытие предложения
unset($stmt);
 
//Закрытие соединения
unset($pdo);
?>

Внимание:Хотя в предварительной обработке предложений не требуется escaping ввода пользователя, вы всегда должны проверять тип и размер данных, полученных из внешних источников, и внедрять соответствующие ограничения для предотвращения использования системных ресурсов.