Articulos Sobre PHP en CableNaranja

Sentencias preparadas en PHP y MySQL con ejemplos, Parte 1

¡Comparte nuestro contenido!

¡Haz clic para puntuar esta entrada!
(Votos: 7 Promedio: 1.6)

Una sentencia preparada o parametrizada, es aquella que permite definir los tipos de datos de los parámetros a utilizar en una consulta. Esta característica, proporciona dos ventajas muy efectivas:

  1. Aumenta la seguridad de nuestras aplicaciones al comprobar los tipos de datos.
  2. La consulta se prepara una única vez, y puede ser ejecutada tantas veces como se requiera.

Las sentencias preparadas se realizan en 3 pasos:

  1. Preparación : Se envía una plantilla de la consulta al servidor, quien revisa la sintaxis y prepara los recursos necesarios.
  2. Vinculación: Se revisan los tipos de datos de cada parametro.
  3. Ejecución: Se ejecuta la consulta todas las veces que sea necesario.

Adicionalmente, las sentencias preparadas son la única característica de PDO que se emula en caso de que el controlador utilizado no las soporte.

¿Por qué son necesarias las sentencias preparadas?

Porque de otra manera estaremos exponiendo nuestras aplicaciones a todo tipo de ataques que explotan la inyección de código para tener acceso a nuestros datos. Siendo este uno de los trucos de ataque básicos en el repertorio de cualquier intruso, se vuelve necesario aprender a utilizarlas.

Preparando el camino

Vamos a crear una base de datos en nuestro servidor de prueba – Recomiendo ampliamente MAMP, por su simpleza de instalación y uso – Dicha base de datos, recibirá el nombre de sentencias y tendra utf8_spanish_ci como cotejamiento.

Sentencias Preparadas en PHP, en CableNaranja
Creando la base de datos

Hagamos una tabla llamada personas con 4 columnas:

Sentencias Preparadas en PHP, en CableNaranja
Creando la tabla

La definición de los campos será la siguiente

Sentencias Preparadas en PHP y MySQL, en CableNaranja
Definiendo los campos

Creando la conexión

Para conectarnos a la Base de datos, vamos a utilizar la extensión MySQLi, que es la extensión mejorada introducida en PHP 5.0.0 y soportada de manera nativa en la versión 5.3.0. Como nota importante, en este tutorial utilizaremos al usuario root únicamente por motivos educativos, recuerde que no es una buena práctica utilizarlo en producción. Hemos de crear una función que contenga el host, usuario, clave y nombre de la base de datos a utilizar. Observe el siguiente código.

function conectar(){
    $host = "localhost";
    $user = "root"; // Para propósitos educativos únicamente
    $clave = "root";
    $bd = "sentencias";
    $conexion = new mysqli($host, $user, $clave, $bd);
    if($conexion->connect_errno){
        exit("Fallo en la conexión al servidor");
    }
    return $conexion;
}

Como observación connect_errno involucra el número de error en caso de ocurrir o false si la conexión fue exitosa. También usamos la función exit para terminar inmediatamente la ejecución del script en caso de error. Este código será guardado en un archivo bajo el nombre de conectar.php y para probarlo, vamos a crear otro archivo llamado index.php, comencemos el archivo con el siguiente código.

<?php
   require_once("conectar.php");
   $enlace = conectar();
?>

Recordemos que la función require_once verifica si el archivo ya había sido incluído para evitar volverlo a incluir. Ahora, coloquemos algo de HTML básico.

<!doctype html>
<html>
    <head>
        <meta lang="es" />
        <meta charset="utf-8" />
        <title>Ejemplo de sentencias preparadas</title>
    </head>
    <body>
        <h1>Lista de personas</h1>
    </body>
</html>

Mostremos cuantas personas registradas llevamos, para ello usaremos una consulta sin parametros muy sencilla:

<?php
   $sqlTotal = "select count(id) as cuantas from personas";
   $resTotal = $enlace->query($sqlTotal);
   $datTotal = $resTotal->fetch_assoc();
?>

Y lo mostramos en un párrafo.

<p>Cantidad de registros: <?= $datTotal["cuantas"] ?></p>

Recordemos que fetch_assoc permite utilizar los nombres de columnas, en lugar de los valores de índice. Por el momento, este es el resultado que podemos ver en nuestro navegador.

Sentencias Preparadas en PHP, en CableNaranja
Resultados hasta ahora

Formulario para insertar datos

Por supuesto, necesitamos un formulario para insertar datos. Incluiremos 3 cajas de texto: 1 de tipo text y 2 de tipo number, además de por supuesto, un botón submit.

<form method="post">
   <fieldset>
      <legend>Nueva Persona</legend>
      <p><label>Nombre</label> <input type="text" name="txtNombre" /></p>
      <p><label>Edad</label> <input type="number" name="txtEdad" value="0" min="0" max="99" /></p>
      <p><label>Estatura</label> <input type="number" name="txtEstatura" value="0.00" min="0" max="2" step=".01" /></p>
      <p><input type="submit" name="ok" value="Guardar datos" /></p>
   </fieldset>
</form>

Como nota adicional, el campo Estatura tiene el valor step establecido en .01 para permitirnos usar números decimales de dos dígitos. Nuestro formulario se ve así en este momento.

Sentencias Preparadas en PHP y Mysql, en CableNaranja
Avance del formulario hasta el momento

Insertar datos con sentencias preparadas

Para que nuestra consulta de selección con todos los resultados funcione, realizaremos la inserción antes del formulario. Como debemos hacerlo sólo al momento de presionar el botón, necesitamos comprobar la existencia de la variable de nuestro botón submit.

<?php
if(isset($_POST["ok"])){
   /// Aquí estará el resto del código               
}
?>

Comencemos por limpiar un sanitizar un poco nuestras variables.

$txtNombre = htmlspecialchars(strip_tags(trim($_POST["txtNombre"])), ENT_QUOTES);
$txtEdad = filter_var($_POST["txtEdad"], FILTER_VALIDATE_INT);
$txtEstatura = filter_var($_POST["txtEstatura"], FILTER_VALIDATE_FLOAT);

Notesé que usamos algunos filtros para nuestras variables numéricas, la lista de filtros completa se puede revisar en la documentación oficial. También usamos las funciones htmlspecialchars y strip_tags en conjunto, para quitar cualquier caracter HTML y minimizar cualquier ataque.

Vamos a revisar que las variables contengan información, usaremos la función strlen para verificar que la longitud de la variable txtNombre no sea 0, revisaremos que tanto txtEdad como txtEstatura contengan un valor diferente a 0. También usaremos un bandera que sólo cambiará a false, si al menos un campo se encuentra vacío. Los mensajes de error, los agregaremos en una variable.

$bandera = true;
$mensaje = "";
if(strlen($txtNombre)==0){
   $mensaje = "<li>Escriba el nombre de la persona</li>";
   $bandera = false;
}
if($txtEdad==0){
   $mensaje .= "<li>¿Qué edad tiene?</li>";
   $bandera = false;
}
if($txtEstatura==0.0){
   $mensaje .= "<li>¿Cuánto mide?</li>";
   $bandera = false;
}
if(!$bandera){
   ?><ol><?= $mensaje ?></ol><?php
}else{
   // Aquí va el siguiente código
}

Antes de insertar cualquier dato, vamos a revisar que no repitamos información, así que nuestra primera consulta preparada en realidad no es la que va a insertar, sino esta. Verificaremos los tres campos necesarios.

$sqlVerifica = "select count(id) as cuantas from personas where nombre = ? and edad = ? and estatura = ?";

Notemos algo importante. Los símbolos ? están substituyendo los valores que estamos buscando, se escriben tal cual y no necesitan comillas de ningún tipo. Estos símbolos son conocidos como marcadores de posición, y se pueden personalizar si es necesario. Debemos además, notar el orden de las variables para no equivocarnos más adelante.

La extensión MySQLi incluye la función real_escape_string que también puede ser utilizada para sanitizar las variables.

$nombre = $enlace->real_escape_string($txtNombre);
$edad = $enlace->real_escape_string($txtEdad);
$estatura = $enlace->real_escape_string($txtEstatura);

Ahora, preparamos nuestro statement con el SQL previo

$stmt = $enlace->prepare($sqlVerifica);

Verificamos las variables, usándolas en la posición exacta cada una, y utilizando los valores de parametro s (string), i (int) y d (decimal) correspondientes.

$stmt->bind_param("sid", $nombre, $edad, $estatura);

Ejecutamos la consulta

$stmt->execute();

Obtenemos resultados y los asignamos a variables, en este caso una sola. Luego cerramos el statement

$stmt->bind_result($cuantas);
$stmt->fetch();
$stmt->close();

Si la cantidad da 0, entonces nos preparamos para insertar los datos, caso contrario, notificamos que ya existen.

if($cuantas==0){
   // Insertar aquí el dato
}else{
   ?><p>La persona que intenta añadir ya existe en nuestra base de datos</p><?php
}

Finalmente, vamos a insertar los datos. Siguiendo casí los mismos pasos, excepto que una vez ejecutada la consulta, cerramos el statement y notificamos que ya hemos terminado.

$sqlInsertar = "insert into personas(nombre, edad, estatura) values(?,?,?)";
$stmtInsert = $enlace->prepare($sqlInsertar);
$stmtInsert->bind_param("sid", $nombre, $edad, $estatura);
$stmtInsert->execute();
$stmtInsert->close();
?><p>¡Gracias por los datos! El ID asignado fue <?= $enlace->insert_id ?></p><?php

El miembro insert_id de la extensión MySQLi, permite saber el último ID insertado. Probemos a insertar un dato

Sentencias Preparadas en PHP y Mysql en CableNaranja
Probando el código…

¡Hemos insertado un dato!

Sentencias Preparadas en PHP y Mysql en CableNaranja
¡Lo logramos!

Y si intentamos repetirlo…

Sentencias Preparadas en PHP y Mysql en CableNaranja
Datos repetidos

Y por ahora eso es todo. Dado que esta es la primera parte, faltan muchos detalles que incluiremos en la siguiente entrega, así como las opciones para editar y eliminar ¡No se pierdan la siguiente parte muy pronto!

¿Te ha resultado? Déjanos saber en los comentarios aquí abajo, en nuestra cuenta de twitter @cablenaranja7 o en nuestra página de facebook.

¡Comparte nuestro contenido!

Entradas relacionadas

2 comentarios en "Sentencias preparadas en PHP y MySQL con ejemplos, Parte 1"

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *