# PHP Data Objects (PDO)

La extensión PDO (PHP Data Objects) permite acceder a distintas bases de datos utilizando las misma funciones, lo que facilita la portabilidad. En PHP 5 existen drivers para acceder a las bases de datos más populares (MySQL, Oracle, MS SQL Server, PostgreSQL, SQLite, Firebird, DB2, Informix, etc).

En esta lección se explica el acceso a MySQL y SQLite mediante PDO. La extensión PDO no evalúa la correción de las consultas SQL.

## Conexión con la base de datos

Para conectar con la base de datos hay que crear una instancia de la clase PDO, que se utiliza en todas las consultas posteriores. En cada página php que incluya consultas a la base de datos es necesario conectar primero con la base de datos.

Si no se puede establecer la conexión con la base de datos, puede deberse a que la base de datos no esté funcionando, a que los datos de usuario no sean correctos, a que no esté activada la extensión pdo o (en el caso de SQLite) que no exista el camino donde se guarda la base de datos.

### Conexión con MySQL

En el caso de MySQL, la creación de la clase PDO incluye el nombre del servidor, el nombre de usuario y la contraseña.

Para poder acceder a MySQL mediante PDO, debe estar activada la extensión php_pdo_mysql en el archivo de configuración php.ini (véase el apartado extensión pdo_mysql en la lección de configuración de Apache y PHP).

    // Esta función se incluiría en la biblioteca de funciones
    function conectaDb()
    {
        try {
            $db = new PDO('mysql:host=localhost', 'root', '');
            $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
            return($db);
        } catch (PDOException $e) {
            cabecera('Error grave');
            print "<p>Error: No puede conectarse con la base de datos.</p>\n";
    //      print "<p>Error: " . $e->getMessage() . "</p>\n";
            pie();
            exit();
        }
    }

    // La conexión se debe realizar en cada página que acceda a la base de datos
    $db = conectaDB();

### Conexión con SQLite 3

En SQLite, no se hace una conexión a un servidor, sino que simplemente se indica el archivo que va a contener la base de datos. En SQLite no hay un servidor que gestiona todas las bases de datos, sino que cada base de datos es un archivo independiente (que debe estar situado en un directorio que exista y en el que el servidor web tenga permisos de escritura).

Para poder utilizar SQLite mediante PDO, debe estar activada la extensión php_pdo_sqlite en el archivo de configuración php.ini (véase el apartado extensión pdo_sqlite en la lección de configuración de Apache y PHP).

    // Esta función se incluiría en la biblioteca de funciones
    function conectaDb()
    {
        try {
            $db = new PDO('sqlite:/tmp/mclibre_baseDeDatos.sqlite');
            return($db);
        } catch (PDOException $e) {
            cabecera('Error grave');
            print "<p>Error: No puede conectarse con la base de datos.</p>\n";
    //      print "<p>Error: " . $e->getMessage() . "</p>\n";
            pie();
            exit();
        }
    }

    // La conexión se debe realizar en cada página que acceda a la base de datos
    $db = conectaDB();

Nota: En las soluciones de los ejercicios proporcionadas en estos apuntes, los archivos se guardan en el directorio /tmp/mclibre. Se debe crear ese directorio para que funcione las soluciones o cambiar

### Conexión configurable

Si se incluyen ambas conexiones en el mismo programa, cada usuario puede elegir la base de datos más conveniente en cada caso. En el ejemplo siguiente, bastaría cambiar el valor de la variable $dbMotor para cambiar la base de datos utilizada.

    // Estas funciones y variables se incluirían en la biblioteca de funciones

    define ('MYSQL', 'MySQL');
    define ('SQLITE', 'SQLite');
    $dbMotor = SQLITE;                                // Base de datos empleada
    if ($dbMotor==MYSQL) {
        define('MYSQL_HOST', 'mysql:host=localhost'); // Nombre de host MYSQL
        define('MYSQL_USUARIO', 'root');              // Nombre de usuario de MySQL
        define('MYSQL_PASSWORD', '');                 // Contraseña de usuario de MySQL
        $dbDb     = 'mclibre_baseDeDatos';            // Nombre de la base de datos
        $dbTabla = $dbDb.'.tabla';                    // Nombre de la tabla
    } elseif ($dbMotor==SQLITE) {
        $dbDb     =  '/tmp/baseDeDatos.sqlite';       // Nombre de la base de datos
        $dbTabla = 'tabla';                           // Nombre de la tabla
    }

    function conectaDb()
    {
        global $dbMotor, $dbDb;

        try {
            if ($dbMotor==MYSQL) {
                $db = new PDO(MYSQL_HOST, MYSQL_USUARIO, MYSQL_PASSWORD);
                $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
            } elseif ($dbMotor==SQLITE) {
                $db = new PDO('sqlite:'.$dbDb);
            }
            return($db);
        } catch (PDOException $e) {
            cabecera('Error grave');
            print "<p>Error: No puede conectarse con la base de datos.</p>\n";
    //        print "<p>Error: " . $e->getMessage() . "</p>\n";
            pie();
            exit();
        }
    }

    // La conexión se debe realizar en cada página que acceda a la base de datos
    $db = conectaDB();

Volver al principio de la página

## Desconexión con la base de datos

Para desconectar con la base de datos hay que destruir el objeto PDO. Si no se destruye el objeto PDO, PHP lo destruye al terminar la página.

    $db = NULL;

Volver al principio de la página

## Consultas a la base de datos

Una vez realizada la conexión a la base de datos, las operaciones se realizan a través de consultas.

El método para efectuar consultas es PDO->query($consulta), que devuelve el resultado de la consulta. Dependiendo del tipo de consulta, el dato devuelto debe tratarse de formas distintas.

En los ejemplos, la consulta se realiza en dos líneas, pero podría estar en una sola:

    // En dos líneas
    $consulta = "SELECT * FROM $dbTabla";
    $result = $db->query($consulta);

    // En una sola línea
    $result = $db->query("SELECT * FROM $dbTabla");

No hay motivo para preferir la primera versión, salvo que se quiera imprimir la consulta mientras se está programando para comprobar que no tiene errores

    $consulta = "SELECT * FROM $dbTabla";
    print "<p>Consulta: $consulta</p>";
    $result = $db->query($consulta);

Si es una consulta que no devuelve registros, sino que simplemente realiza una acción que puede tener éxito o no (por ejemplo, insertar un registro) se puede utilizar la función para sacar un mensaje diciendo que todo ha ido bien (o no). Por ejemplo,

    // CONSULTA DE INSERCIÓN DE REGISTRO
    $db = conectaDb();
    $consulta = "INSERT INTO $dbTabla
        values (NULL, $nombre, $apellidos, $telefono, $correo)";
    if ($db->query($consulta)) {
        print "<p>Registro creado correctamente.</p>\n";
    } else {
        print "<p>Error al crear el registro.<p>";
    }
    $db = NULL;

Pero si la consulta devuelve registros, el método devuelve un tipo de variable llamado recurso que no se puede acceder directamente, pero que se puede recorrer con un bucle foreach(),

    $db = conectaDb();

    $consulta = "SELECT * FROM $dbTabla";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
        foreach ($result as $valor) {
            print "<p>$valor[nombre] $valor[apellidos]</p>\n";
        }
    }

Volver al principio de la página

### Seguridad en las consultas

Los ataques malintencionados más habituales son las inyecciones de SQL, que consisten en enviar en los campos de formulario partes de una consulta SQL de manera que PHP ejecute una consulta distinta a la preparada por el programador de la página.

Para evitar este tipo de ataques, es necesario utilizar el método PDO->quote($consulta) antes de incluir en la consulta datos enviados por el usuario. Este método debe aplicarse a cualquier dato recibido del usuario que vaya a incluirse en una consulta. Las funciones del ejemplo siguiente aplican, además del método quote, todas las precacuciones comentadas en la lección de recogida de datos, para recoger datos sueltos o matrices de una dimensión.

Por otro lado, las funciones recogeParaConsulta() y recogeMatrizParaConsulta() del ejemplo también llaman a una función recorta() que recorta la longitud de los campos. Esta función no es estrictamente necesaria, pero se utiliza aquí para que los programas den el mismo resultado en MySQL y SQLite. Al crear un campo de tipo cadena en una tabla en MySQL o SQLite se indica la longitud del campo. Pero resulta que si se intenta guardar una cadena de mayor longitud, MySQL recorta la cadena a la longitud del campo pero SQLite no lo hace. Utilizando la función recorta(), el resultado no dependerá de la base de datos utilizada.

    // Estas funciones y variables se incluirían en la biblioteca de funciones

    $recorta = array(
        'nombre'    => $tamNombre,
        'apellidos' => $tamApellidos,
        'telefono'  => $tamTelefono,
        'correo'    => $tamCorreo);

    function recorta($campo, $cadena)
    {
        global $recorta;

        $tmp = isset($recorta[$campo]) ? substr($cadena, 0, $recorta[$campo]) : $cadena;
        return $tmp;
    }

    function recogeParaConsulta($db, $var, $var2='')
    {
        $tmp = (isset($_REQUEST[$var]) && ($_REQUEST[$var]!='')) ?
            strip_tags(trim(htmlspecialchars($_REQUEST[$var]))) : strip_tags(trim(htmlspecialchars($var2)));
        if (get_magic_quotes_gpc()) {
            $tmp = stripslashes($tmp);
        }
        $tmp = recorta($var, $tmp);
        if (!is_numeric($tmp)) {
            $tmp = $db->quote($tmp);
        }
        return $tmp;
    }

    function recogeMatrizParaConsulta($db, $var)
    {
        $tmpMatriz = array();
        if (isset($_REQUEST[$var]) && is_array($_REQUEST[$var])) {
            foreach ($_REQUEST[$var] as $indice => $valor) {
                $tmp = strip_tags(trim(htmlspecialchars($indice)));
                if (get_magic_quotes_gpc()) {
                    $tmp = stripslashes($tmp);
                }
                $tmp = recorta($var, $tmp);
                if (!is_numeric($tmp)) {
                    $tmp = $db->quote($tmp);
                }
                $indiceLimpio = $tmp;

                $tmp = strip_tags(trim(htmlspecialchars($valor)));
                if (get_magic_quotes_gpc()) {
                    $tmp = stripslashes($tmp);
                }
                $tmp = recorta($var, $tmp);
                if (!is_numeric($tmp)) {
                    $tmp = $db->quote($tmp);
                }
                $valorLimpio  = $tmp;

                $tmpMatriz[$indiceLimpio] = $valorLimpio;
            }
        }
        return $tmpMatriz;
    }

Las funciones recogeParaConsulta() y recogeMatrizParaConsulta() añaden comillas al principio y al final, pero en algunos casos los datos insertados en las consultas no deben estar entre comillas, como se comenta más adelante. Esta función quita esas comillas:

    // Estas función se incluiría en la biblioteca de funciones

    function quitaComillasExteriores($var)
    {
        if (is_string($var)) {
            if (isset($var[0])&&($var[0]=="'")) {
                $var = substr($var, 1, strlen($var)-1);
            }
            if (isset($var[strlen($var)-1])&&($var[strlen($var)-1]=="'")) {
                $var = substr($var, 0, strlen($var)-1);
            }
        }
        return $var;
    }

Volver al principio de la página

### Consultas CREATE DATABASE, DROP DATABASE

Nota: En el caso de utiliza SQLite, no tiene sentido crear o borrar la base de datos ya que con SQLite cada base de datos es un fichero distinto y al conectar con la base de datos ya se dice con qué archivo se va a trabajar y se crea en caso necesario.

Para crear una base de datos, se utiliza la consulta CREATE DATABASE.

    // CONSULTA DE CREACIÓN DE BASE DE DATOS
    $db = conectaDb();
    $consulta = "CREATE DATABASE $dbDb";
    if ($db->query($consulta)) {
        print "<p>Base de datos creada correctamente.</p>\n";
    } else {
        print "<p>Error al crear la base de datos.</p>\n";
    }
    $db = NULL;

Para borrar una base de datos, se utiliza la consulta DROP DATABASE.

    // CONSULTA DE BORRADO DE BASE DE DATOS
    $db = conectaDb();
    $consulta = "DROP DATABASE $dbDb";
    if ($db->query($consulta)) {
        print "<p>Base de datos borrada correctamente.</p>\n";
    } else {
        print "<p>Error al borrar la base de datos.</p>\n";
    }
    $db = NULL;

Volver al principio de la página

### Consultas CREATE TABLE, DROP TABLE, INSERT INTO, UPDATE, DELETE FROM

Para crear una tabla, se utiliza la consulta CREATE TABLE. Las consultas de creación de tabla suelen ser específicas de cada base de datos.

    // CONSULTA DE CREACIÓN DE TABLA EN MYSQL
    $db = conectaDb();
    $consulta = "CREATE TABLE $dbTabla (
        id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
        nombre VARCHAR($tamNombre),
        apellidos VARCHAR($tamApellidos),
        telefono VARCHAR($tamTelefono),
        correo VARCHAR($tamCorreo),
        PRIMARY KEY(id)
        )";
    if ($db->query($consulta)) {
        print "<p>Tabla creada correctamente.</p>\n";
    } else {
        print "<p>Error al crear la tabla.</p>\n";
    }
    $db = NULL;

    // CONSULTA DE CREACIÓN DE TABLA EN SQLite
    $db = conectaDb();
    $consulta = "CREATE TABLE $dbTabla (
        id INTEGER PRIMARY KEY,
        nombre VARCHAR($tamNombre),
        apellidos VARCHAR($tamApellidos),
        telefono VARCHAR($tamTelefono),
        correo VARCHAR($tamCorreo)
        )";
    if ($db->query($consulta)) {
        print "<p>Tabla creada correctamente.</p>\n";
    } else {
        print "<p>Error al crear la tabla.</p>\n";
    }
    $db = NULL;

Para borrar una tabla, se utiliza la consulta DROP TABLE.

    // CONSULTA DE BORRADO DE TABLA
    $db = conectaDb();
    $consulta = "DROP TABLE $dbTabla";
    if ($db->query($consulta)) {
        print "<p>Tabla de $dbTabla borrada correctamente.</p>\n";
    } else {
        print "<p>Error al borrar la tabla de $dbTabla.</p>\n";
    }
    $db = NULL;

Para añadir un registro a una tabla, se utiliza la consulta INSERT INTO.

    // CONSULTA DE INSERCIÓN DE REGISTRO
    $db = conectaDb();
    $consulta = "INSERT INTO $dbTabla
        values (NULL, $nombre, $apellidos, $telefono, $correo)";
    if ($db->query($consulta)) {
        print "<p>Registro creado correctamente.</p>\n";
    } else {
        print "<p>Error al crear el registro.<p>";
    }
    $db = NULL;

Nota: Antes de insertar datos provinientes de un formulario, las cadenas deben haberse tratado para evitar inyecciones de SQL.

Para modificar un registro a una tabla, se utiliza la consulta UPDATE.

    // CONSULTA DE MODIFICACIÓN DE REGISTRO
    $db = conectaDb();
    $consulta = "UPDATE $dbTabla SET nombre=$nombre,
                apellidos=$apellidos, telefono=$telefono, correo=$correo
                WHERE id=$modificar";
    if ($db->query($consulta)) {
        print "<p>Registro modificado correctamente.</p>\n";
    } else {
        print "<p>Error al modificar el registro.</p>\n";
    }
    $db = NULL;

Nota: Antes de insertar datos provinientes de un formulario, las cadenas deben haberse tratado para evitar inyecciones de SQL.

Para borrar un registro de una tabla, se utiliza la consulta DELETE FROM.

    // CONSULTA DE BORRADO DE REGISTRO
    $db = conectaDb();
    $consulta = "DELETE FROM $dbTabla WHERE id=$indice";
    if ($db->query($consulta)) {
        print "<p>Registro borrado correctamente.</p>\n";
    } else {
        print "<p>Error al borrar el registro.</p>\n";
    }
    $db = NULL;

Volver al principio de la página

### Consulta SELECT

Para obtener registros que cumplan determinados criterios se utiliza la consulta SELECT.

    // CONSULTA DE SELECCIÓN DE REGISTROS
    $db = conectaDb();
    $consulta = "SELECT * FROM $dbTabla";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
        print "<p>Consulta ejecutada.</p>\n";
    }
    $db = NULL;

Para acceder a los registros devueltos por la consulta, se puede utilizar un bucle foreach.

    // CONSULTA DE SELECCIÓN DE REGISTROS
    $db = conectaDb();
    $consulta = "SELECT * FROM $dbTabla";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
        foreach ($result as $valor) {
            print "<pre>\n";
            print_r($valor);
            print "</pre>\n";
        }
    }
    $db = NULL;

O también se puede utilizar la función PDOStatement->fetch()

    // CONSULTA DE SELECCIÓN DE REGISTROS
    $db = conectaDb();
    $consulta = "SELECT * FROM $dbTabla";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
        while ($fila = $result->fetch()) {
            print "<pre>\n";
            print_r($fila);
            print "</pre>\n";
        }
    }
    $db = NULL;

El problema es que si la consulta no devuelve ningún registro, estos dos métodos no escribirían nada. Por ello se recomienda hacer primero una consulta que cuente el número de resultados de la consulta y, si es mayor que cero, hacer la consulta.

El ejemplo siguiente utiliza la función PDOStatement->fetchColumn(), que devuelve la primera columna del primer resultado (que en este caso contiene el número de registros de la consulta).

    // CONSULTA DE SELECCIÓN DE REGISTROS
    $db = conectaDb();
    $consulta = "SELECT COUNT(*) FROM $dbTabla";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } elseif ($result->fetchColumn()==0) {
        print "<p>No se ha creado todavía ningún registro en la $dbTabla.</p>\n";
    } else {
        $consulta = "SELECT * FROM $dbTabla";
        $result = $db->query($consulta);
        if (!$result) {
            print "<p>Error en la consulta.</p>\n";
        } else {
            foreach ($result as $valor) {
                print "<pre>\n";
                print_r($valor);
                print "</pre>\n";
            }
        }
    }
    $db = NULL;

La consulta SELECT permite efectuar búsquedas en cadenas utilizando el condicional LIKE o NOT LIKE y los comodines _ (cualquier carácter) o % (cualquier número de caracteres). La primera consulta del ejemplo siguiente devolvería todos los registros en los que el primer apellido es Pérez, mientras que la segunda consulta devolvería todos los registros en los que el primer o segundo apellido es Pérez.

    // CONSULTA DE SELECCIÓN DE REGISTROS
    $db = conectaDb();
    $consulta = "SELECT COUNT(*) FROM $dbTabla WHERE apellidos LIKE '$apellidos%'";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
        print "<p>Se han encontrado ".$result->fetchColumn())." registros.</p>\n";
    }
    $db = NULL;

    // CONSULTA DE SELECCIÓN DE REGISTROS
    $db = conectaDb();
    $consulta = "SELECT COUNT(*) FROM $dbTabla WHERE apellidos LIKE '%$apellidos%'";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
        print "<p>Se han encontrado ".$result->fetchColumn())." registros.</p>\n";
    }
    $db = NULL;

En este caso los datos recogidos mediante la función recogeParaConsulta() no deben llevar comillas ni al principio ni al final, por lo que deberían pasarse por la función quitaComillasExteriores().

Se pueden también realizar consultas de unión entre varias tablas (el ejemplo está sacado del ejercicio de Biblioteca):

    // CONSULTA DE UNIÓN DE TABLAS
    $db = conectaDb();
    $consulta = "SELECT $dbPrestamos.id AS id, $dbUsuarios.nombre as nombre,
        $dbUsuarios.apellidos as apellidos, $dbObras.titulo as titulo,
        $dbPrestamos.prestado as prestado, $dbPrestamos.devuelto as devuelto
        FROM $dbPrestamos, $dbUsuarios, $dbObras
        WHERE $dbPrestamos.id_usuario=$dbUsuarios.id AND
        $dbPrestamos.id_obra=$dbObras.id and $dbPrestamos.devuelto='0000-00-00'
        ORDER BY $campo $orden";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } else {
       ...
    }
    $db = NULL;

Volver al principio de la página

### Resumen

Los siguientes cuadros resumen las funciones y consultas vistas en esta lección:

    // FUNCIONES DE RECOGIDA DE DATOS QUE SE VAN A UTILIZAR EN UNA CONSULTA

    $recorta = array(
        'nombre'   => $tamNombre,
        'apellidos'=> $tamApellidos,
        'telefono' => $tamTelefono,
        'correo'   => $tamCorreo);

    function recorta($campo, $cadena)
    {
        global $recorta;

        $tmp = isset($recorta[$campo]) ? substr($cadena, 0, $recorta[$campo]) : $cadena;
        return $tmp;
    }

    function recogeParaConsulta($db, $var, $var2='')
    {
        $tmp = (isset($_REQUEST[$var]) && ($_REQUEST[$var]!='')) ?
            strip_tags(trim(htmlspecialchars($_REQUEST[$var]))) : strip_tags(trim(htmlspecialchars($var2)));
        if (get_magic_quotes_gpc()) {
            $tmp = stripslashes($tmp);
        }
        $tmp = recorta($var, $tmp);
        if (!is_numeric($tmp)) {
            $tmp = $db->quote($tmp);
        }
        return $tmp;
    }

    function recogeMatrizParaConsulta($db, $var)
    {
        $tmpMatriz = array();
        if (isset($_REQUEST[$var]) && is_array($_REQUEST[$var])) {
            foreach ($_REQUEST[$var] as $indice => $valor) {
                $tmp = strip_tags(trim(htmlspecialchars($indice)));
                if (get_magic_quotes_gpc()) {
                    $tmp = stripslashes($tmp);
                }
                $tmp = recorta($var, $tmp);
                if (!is_numeric($tmp)) {
                    $tmp = $db->quote($tmp);
                }
                $indiceLimpio = $tmp;

                $tmp = strip_tags(trim(htmlspecialchars($valor)));
                if (get_magic_quotes_gpc()) {
                    $tmp = stripslashes($tmp);
                }
                $tmp = recorta($var, $tmp);
                if (!is_numeric($tmp)) {
                    $tmp = $db->quote($tmp);
                }
                $valorLimpio  = $tmp;

                $tmpMatriz[$indiceLimpio] = $valorLimpio;
            }
        }
        return $tmpMatriz;
    }

    // Nota: La función recogeParaConsulta añade comillas al principio y al final.
    // Si por algún motivo hay que quitar esas comillas, se puede utilizar esta función:

    function quitaComillasExteriores($var)
    {
        if (is_string($var)) {
            if (isset($var[0])&&($var[0]=="'")) {
                $var = substr($var, 1, strlen($var)-1);
            }
            if (isset($var[strlen($var)-1])&&($var[strlen($var)-1]=="'")) {
                $var = substr($var, 0, strlen($var)-1);
            }
        }
        return $var;
    }

    // CONECTAR CON LA BASE DE DATOS (MysQL y SQLite)

    define ('MYSQL', 'MySQL');
    define ('SQLITE', 'SQLite');
    $dbMotor = SQLITE;                          // Base de datos empleada
    if ($dbMotor==MYSQL) {
        define('MYSQL_HOST', 'mysql:host=localhost'); // Nombre de host MYSQL
        define('MYSQL_USUARIO', 'root');        // Nombre de usuario de MySQL
        define('MYSQL_PASSWORD', '');           // Contraseña de usuario de MySQL
        $dbDb    = 'baseDeDatos';               // Nombre de la base de datos
        $dbTabla = $dbDb.'.tabla';              // Nombre de la tabla
    } elseif ($dbMotor==SQLITE) {
        $dbDb    = '/tmp/baseDeDatos.sqlite3';  // Nombre y ruta de la base de datos
        $dbTabla = 'tabla';                     // Nombre de la tabla
    }

    function conectaDb()
    {
        global $dbMotor, $dbDb;

        try {
            if ($dbMotor==MYSQL) {
                $db = new PDO(MYSQL_HOST, MYSQL_USUARIO, MYSQL_PASSWORD);
                $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
            } elseif ($dbMotor==SQLITE) {
                $db = new PDO('sqlite:'.$dbDb);
            }
            return($db);
        } catch (PDOException $e) {
            cabecera('Error grave');
            print "<p>Error: No puede conectarse con la base de datos.</p>\n";
            pie();
            exit();
        }
    }

    $db = conectaDB();

    // DESTRUIR LA CONEXIÓN CON LA BASE DE DATOS
    $db = NULL;

    // EJEMPLOS DE CONSULTAS

    // Borrar la base de datos (innecesario en SQLite)
    $consulta = "DROP DATABASE $dbDb"

    // Crear la base de datos (innecesario en SQLite)
    $consulta = "CREATE DATABASE $dbDb"

    // Crear una tabla en MySQL o en SQLite
    $consultaMysql = "CREATE TABLE $dbTabla (
        id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
        nombre VARCHAR($tamNombre),
        apellidos VARCHAR($tamApellidos),
        telefono VARCHAR($tamTelefono),
        correo VARCHAR($tamCorreo),
        PRIMARY KEY(id)
        )";

    $consultaSqlite = "CREATE TABLE $dbTabla (
        id INTEGER PRIMARY KEY,
        nombre VARCHAR($tamNombre),
        apellidos VARCHAR($tamApellidos),
        telefono VARCHAR($tamTelefono),
        correo VARCHAR($tamCorreo)
        )";

    // Borrar una tabla
    $consulta = "DROP TABLE $dbTabla";

    // Añadir un dato
    $consulta = "INSERT INTO $dbTabla
                 values (NULL, $nombre, $apellidos, $telefono, $correo)";

    // Contar todos los registros
    $consulta = "SELECT COUNT(*) FROM $dbTabla";

    // Seleccionar todos los registros
    $consulta = "SELECT * FROM $dbTabla";

    // Seleccionar todos los registros con algún criterio de ordenación
    $consulta = "SELECT * FROM $dbTabla ORDER BY $campo $orden";

    // Borrar un registro
    $consulta = "DELETE FROM $dbTablad WHERE id=$indice";

    // Borrar varios registros
    $id = recogeMatrizParaConsulta($db, 'id');
    foreach ($id as $indice => $valor) {
        $consulta = "DELETE FROM $dbTabla WHERE id=$indice";
        ...
    }

    // Buscar registros
    $consulta = "SELECT * FROM $dbTabla WHERE nombre LIKE '%$nombre%'
        AND apellidos LIKE '%$apellidos%' AND telefono LIKE '%$telefono%'
        AND correo LIKE '%$correo%' ORDER BY $campo $orden";

    // Modificar un registro
    $consulta = "UPDATE $dbTabla SET nombre=$nombre,
                 apellidos=$apellidos, telefono=$telefono, correo=$correo
                 WHERE id=$id";

    // EJECUTAR CONSULTAS

    $result = $db->query($consulta);

    // Para comprobar si el resultado es vacío:
    $consulta = "SELECT COUNT(*) FROM $dbTabla";
    $result = $db->query($consulta);
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
    } elseif ($result->fetchColumn()==0) {
        print "<p>No se ha creado todavía ningún registro en la agenda.</p>\n";
    } else {
        $consulta = "SELECT * FROM $dbTabla ORDER BY $campo $orden";
        $result = $db->query($consulta);
        if (!$result) {
            print "<p>Error en la consulta.</p>\n";
        } else {
     ...

    // Si el resultado de la consulta pueden ser varias filas:
    foreach ($result as $valor) {
        print "<p>Nombre: $valor[nombre] - Apellidos: $valor[apellidos]</p>\n";
    }

Volver al principio de la página

## Consultas preparadas

En el apartado anterior de este mismo tema, la seguridad en las consultas frente a ataques de inyección SQL se consigue tratando cuidadosamente cualquier dato introducido por el usuario. Otro enfoque para evitar ataques de inyección SQL es el uso de sentencias preparadas, en las que se pueden tener menos precauciones con las entradas de los usuarios, ya que es el propio PHP el que se encarga de "desinfectar" los datos en caso necesario.

### Consultas preparadas a la base de datos

Una vez realizada la conexión a la base de datos, las operaciones se pueden realizar también a través de consultas preparadas

El método para efectuar consultas es primero preparar la consulta con PDO->prepare($consulta) y después ejecutarla con PDO->execute(array(parámetros)), que devuelve el resultado de la consulta. Dependiendo del tipo de consulta, el dato devuelto debe tratarse de formas distintas, como se ha explicado en el apartado anterior.

En los ejemplos, la consulta se realiza en tres líneas, pero podría estar en sólo dos:

    // En tres líneas
    $consulta = "SELECT * FROM $dbTabla";
    $result = $db->prepare($consulta);
    $result->execute();

    // En dos líneas
    $result = $db->prepare("SELECT * FROM $dbTabla");
    $result->execute();

No hay motivo para preferir la primera versión, salvo que se quiera imprimir la consulta mientras se está programando para comprobar que no tiene errores

    $consulta = "SELECT * FROM $dbTabla";
    print "<p>Consulta: $consulta</p>";
    $result = $db->prepare($consulta);
    $result->execute();

Las consultas preparadas pueden incluir variables. Existen varias notaciones, una de ellas utiliza el interrogante (?) para indicar la posición de las variables, variable cuyo valor se especifica al ejecutar la consulta (enviando una matriz de una dimensión formada por los datos que sustituyen a los interrogantes en el mismo orden en que aparecen), como muestra el ejemplo siguiente

    $usuario    = $_REQUEST['usuario'];
    $contraseña = $_REQUEST['contraseña'];

    $consulta = "SELECT COUNT(*) FROM $dbTabla
        WHERE user=?
        AND password=?";
    $result = $db->prepare($consulta);
    $result->execute(array($usuario, $contraseña));
    if (!$result) {
        print "<p>Error en la consulta.</p>\n";
     ...

Aunque no vayan a causar problermas en las consultas, sigue siendo conveniente tratar los datos recibidos para eliminar los espacios en blanco iniciales y finales, tratar los carácteres especiales del html, etc., como se vió en la lección de recogida de datos.

Una vez realizada la consulta, el tratamiento de la respuesta sería el mismo que se ha visto en el apartado anterior.

Volver al principio de la página

Esta obra está bajo una licencia de Creative Commons.