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

Tutoriales básicos de PHP

Tutoriales avanzados de PHP

PHP y MySQL

Manual de referencia PHP

Sentencias preparadas en PHP

En este tutorial, aprenderá cómo usar las sentencias preparadas en MySQL con PHP.

¿Qué es una sentencia preparada?

Una sentencia preparada (también conocida como sentencia parametrizada) es solo una plantilla de consulta SQL que contiene marcadores de posición en lugar de valores de parámetros reales. Al ejecutar la sentencia, estos marcadores de posición se reemplazan por valores reales.

MySQLi admite el uso de marcadores de posición anónimos (?), como se muestra a continuación:

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

PDO admite marcadores de posición anónimos (?), y marcadores de posición nombrados. Los marcadores de posición nombrados comienzan con dos puntos (:) seguidos de un identificador, como se muestra a continuación:

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

La ejecución de las sentencias preparadas incluye dos fases: preparar y ejecutar.

  • Preparar - En la fase de preparación, se crea una plantilla de sentencia SQL y se envía al servidor de base de datos. El servidor analiza la plantilla de la sentencia, realiza comprobaciones de sintaxis y optimización de consultas, y la almacena para su uso posterior.

  • Ejecutar - Durante la ejecución, los valores de los parámetros se enviarán al servidor. El servidor crea una sentencia a partir de la plantilla de la sentencia y estos valores para ejecutarla.

Las sentencias preparadas son muy útiles, especialmente cuando se utilizan diferentes valores (por ejemplo, una serie de instrucciones) para ejecutar una instrucción INSERT específica varias veces. A continuación, se describen algunos de los principales beneficios de su uso.

Ventajas de usar sentencias preparadas

Una sentencia preparada se puede ejecutar repetidamente de manera eficiente, ya que solo se analiza una vez y se puede ejecutar múltiples veces. Además, ya que solo se necesitan transmitir los valores de los marcadores al servidor de base de datos en cada ejecución, en lugar de la sentencia SQL completa, también puede reducir al máximo el uso del ancho de banda.

Las sentencias preparadas también proporcionan una protección poderosa para evitarInyección SQLDebido a que los valores de los parámetros no se insertan directamente en la cadena de consulta SQL. Se envían los valores de los parámetros y la consulta por separado al servidor de base de datos, por lo que no los interfiere. Después de analizar la plantilla de la sentencia, el servidor utiliza directamente estos valores en la ejecución. Por eso las sentencias preparadas son difíciles de equivocar, y se consideran uno de los elementos más críticos de la seguridad de la base de datos.

El siguiente ejemplo le mostrará cómo funciona realmente la sentencia preparada:

Ejemplo: método orientado a procedimientos

<?php
/* Intentar conectar al servidor MySQL. Suponiendo que está ejecutando MySQL.
Servidor con configuración predeterminada (usuario sin contraseña "root") */
$link = mysqli_connect("localhost", "root", "", "demo");
 
//Revisión de conexión
if($link === false){
    die("Error: No se puede conectar. ". mysqli_connect_error());
}
 
//Usar sentencia preparada
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = mysqli_prepare($link, $sql)){
    //Vincular variables como parámetros a la sentencia preparada
    mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
    
    /* Establecer el valor de los parámetros y ejecutar, esta sentencia inserta otra fila. */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "[email protected]";
    mysqli_stmt_execute($stmt);
    
    /* Establecer el valor de los parámetros y ejecutar la sentencia de inserción de filas. */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "[email protected]";
    mysqli_stmt_execute($stmt);
    
    echo "Registro insertado con éxito.";
} else{
    echo "Error: No se puede preparar la consulta: $sql. " . mysqli_error($link);
}
 
//Cerrar sentencia
mysqli_stmt_close($stmt);
 
//Cerrar conexión
mysqli_close($link);
?>

Ejemplo: método orientado a objetos

<?php
/* Intentar conectar al servidor MySQL. Suponiendo que está ejecutando MySQL.
Servidor con configuración predeterminada (usuario sin contraseña "root") */
$mysqli = new mysqli("localhost", "root", "", "demo");
 
//Revisión de conexión
if($mysqli === false){
    die("Error: No se puede conectar. ". $mysqli->connect_error);
}
 
// Usar sentencia preparada
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = $mysqli->prepare($sql)){
    // Vincular variables como parámetros a la sentencia preparada
    $stmt->bind_param("sss", $first_name, $last_name, $email);
    
    /* Establecer el valor de los parámetros y ejecutar.
    Ejecutar la sentencia nuevamente para insertar otra fila */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "[email protected]";
    $stmt->execute();
    
    /* Establecer valores de parámetros y ejecutar
        Sentencia para insertar la fila */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "Se ha insertado el registro con éxito.";
} else{
    echo "Error: No se pudo preparar la consulta: $sql. " . $mysqli"->error;
}
 
//Cerrar sentencia
$stmt->close();
 
//Cerrar conexión
$mysqli->close();
?>

Ejemplo: Método PDO

<?php
/* Intentar conectar al servidor MySQL. Suponiendo que está ejecutando MySQL.
Servidor con configuración predeterminada (usuario sin contraseña "root") */
try{
    $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", "");
    // Establecer el modo de error PDO en excepciones
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
    die("Error: No se puede conectar. " . $e->getMessage());
}
 
//Intentar ejecutar la consulta de inserción
try{
    //Usar sentencia preparada
    $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)";
    $stmt = $pdo->prepare($sql);
    
    //Enlazar parámetros a la sentencia
    $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);
    
    /* Establecer valores de parámetros y ejecutar,
      Ejecutar la sentencia nuevamente para insertar otra fila */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "[email protected]";
    $stmt->execute();
    
    /* Establecer valores de parámetros y ejecutar
        Sentencia para insertar la fila */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "Registro insertado con éxito.";
} catch(PDOException $e){
    die("Error: No se puede preparar/Ejecutar consulta: $sql. " . $e->getMessage());
}
 
// Cerrar sentencia
unset($stmt);
 
//Cerrar conexión
unset($pdo);
?>

Como puede ver en el ejemplo anterior, solo preparamos una vez la sentencia INSERT, pero ejecutamos la sentencia varias veces pasando diferentes conjuntos de parámetros.

Uso del código (estilo de programación)

En la sentencia SQL INSERT del ejemplo anterior, el signo de interrogación se utiliza comofirst_name,last_nameyemailMarcador de posición para el valor del campo.

La función mysqli_stmt_bind_param() une variables a los marcadores de posición (?) en la plantilla de sentencia SQL. Los marcadores de posición (?) se reemplazarán con los valores reales almacenados en las variables en el momento de la ejecución. La cadena de definición de tipo proporcionada como segundo parámetro, es decir, la cadena 'sss' especifica que cada tipo de variable de enlace es string (cadena).

La cadena de definición de tipo especifica el tipo de datos de la variable de enlace correspondiente, y los parámetros tienen los siguientes cuatro tipos:

  • i - integer (entero)

  • d - double (punto flotante de doble precisión)

  • s - string (cadena)

  • b - BLOB (binary large object: objeto binario grande)

El número de variables de enlace y el número de caracteres en la definición de tipo de cadena deben coincidir con el número de marcadores de posición en la plantilla de sentencia SQL.

Utilizar las entradas recibidas a través de un formulario web

Si recuerda el capítulo anterior, ya hemos creado un formulario HTML paraInsertar datos en la base de datosAquí, expandiremos el ejemplo ejecutando una sentencia de preprocesamiento. Puede utilizar el mismo formulario HTML para probar los siguientes ejemplos de inserción de scripts, pero asegúrese de que el atributo action del formulario utilice el nombre de archivo correcto.

Este es el código PHP actualizado para insertar datos. Si observa el ejemplo, notará que no usamos mysqli_real_escape_string() como en el ejemplo del capítulo anterior. Dado que en las sentencias preparadas, la entrada del usuario nunca se reemplaza directamente en la cadena de consulta, no es necesario escaparlas correctamente.

Ejemplo: método orientado a procedimientos

<?php
/* Intentar conectar al servidor MySQL. Suponiendo que está ejecutando MySQL.
Servidor con configuración predeterminada (usuario sin contraseña "root") */
$link = mysqli_connect("localhost", "root", "", "demo");
 
//Revisión de conexión
if($link === false){
    die("Error: No se puede conectar. ". mysqli_connect_error());
}
 
//Usar sentencia preparada
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = mysqli_prepare($link, $sql)){
    //Vincular variables a la sentencia preparada como parámetros
    mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
    
    //Establecer parámetros
    $first_name = $_REQUEST['first_name'];
    $last_name = $_REQUEST['last_name'];
    $email = $_REQUEST['email'];
    
    //Intentar ejecutar la sentencia preparada}}
    if(mysqli_stmt_execute($stmt)){
        echo "Registro insertado con éxito.";
    } else{
        echo "Error: No se puede ejecutar la consulta: $sql ". $mysqli_error($link);
    }
} else{
    echo "Error: No se puede ejecutar la consulta: $sql ". $mysqli_error($link);
}
 
// Cerrar sentencia
mysqli_stmt_close($stmt);
 
//Cerrar conexión
mysqli_close($link);
?>

Ejemplo: método orientado a objetos

<?php
/* Intentar conectar al servidor MySQL. Suponiendo que está ejecutando MySQL.
Servidor con configuración predeterminada (usuario sin contraseña "root") */
$mysqli = new mysqli("localhost", "root", "", "demo");
 
//Revisión de conexión
if($mysqli === false){
    die("Error: No se puede conectar. ". $mysqli->connect_error);
}
 
//Usar sentencia preparada
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = $mysqli->prepare($sql)){
    //Vincular variables como parámetros a la sentencia preparada
    $stmt->bind_param("sss", $first_name, $last_name, $email);
    
    //Configuración de parámetros
    $first_name = $_REQUEST['first_name'];
    $last_name = $_REQUEST['last_name'];
    $email = $_REQUEST['email'];
    
    //Intentar ejecutar la sentencia preparada}}
    if($stmt->execute());
        echo "Registro insertado con éxito.";
    } else{
        echo "Error: No se puede ejecutar la consulta: $sql. " . $mysqli->error;
    }
} else{
    echo "Error: No se puede ejecutar la consulta: $sql. " . $mysqli->error;
}
 
//Cerrar sentencia
$stmt->close();
 
//Cerrar conexión
$mysqli->close();
?>

Ejemplo: Método PDO

<?php
/* Intentar conectar al servidor MySQL. Suponiendo que está ejecutando MySQL.
Servidor con configuración predeterminada (usuario sin contraseña "root") */
try{
    $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", "");
    //Establecer el modo de error PDO en excepciones
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
    die("Error: No se puede conectar. " . $e->getMessage());
}
 
//Intentar ejecutar la consulta de inserción
try{
    //Usar sentencia preparada
    $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)";
    $stmt = $pdo->prepare($sql);
    
    // Enlazar parámetros a la sentencia
    $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);
    
    // Ejecutar sentencia preparada
    $stmt->execute();
    echo "Registro insertado con éxito.";
} catch(PDOException $e){
    die("Error: No se puede preparar/Ejecutar consulta $sql. " . $e->getMessage());
}
 
//Cerrar sentencia
unset($stmt);
 
//Cerrar conexión
unset($pdo);
?>

Nota:A pesar de que no es necesario escapar la entrada del usuario en la fase de preprocesamiento, siempre debe verificar el tipo y el tamaño de los datos recibidos de fuentes externas e implementar limitaciones adecuadas para evitar el uso de recursos del sistema.