English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
En este tutorial, aprenderá cómo usar las sentencias preparadas en MySQL con PHP.
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.
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:
<?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); ?>
<?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(); ?>
<?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.
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.
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.
<?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); ?>
<?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(); ?>
<?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.