Sie sind auf Seite 1von 67

Php orientado a objetos, parte 11:

PDO, Guardar registros en una


base de datos
15 enero, 2013 Ferchu MySQL, Php21 comentarios

Bueno, siguiendo con el tema de mi anterior posteo, Php orientado a objetos,


parte 10: PDO, Conectarse a una base de datos, hoy les mostrar cmo guardar
registros en una base de datos.

En primer lugar, seguir con la misma base de datos de la vez pasada, si no


viste el anterior posteo haba creado una base de datos con el nombre batman,
de esta forma:

CREATEDATABASEbatman;

Ahora crear una tabla para guardar los personajes de esta serie:

CREATETABLEpersonaje(
idtinyint(3)unsignednotnullauto_incrementprimarykey,
nombrevarchar(255)notnull,
descripciontextnotnull
)

Bueno, hasta ac nada nuevo, cre una base de datos con una sola tabla que
guardar los nombres y una descripcin de los personajes, y por supuesto
tendr un id o clave primaria.

Ahora, para probar mis consultas crear dos clases. Una ser una clase para
conectarse a la base de datos, la misma que us en el posteo anterior:

<?php
classConexionextendsPDO{
private$tipo_de_base='mysql';
private$host='localhost';
private$nombre_de_base='batman';
private$usuario='root';
private$contrasena='';
publicfunction__construct(){
//SobreescriboelmtodoconstructordelaclasePDO.
try{
parent::__construct($this>tipo_de_base.':host='.$this
>host.';dbname='.$this>nombre_de_base,$this>usuario,$this
>contrasena);
}catch(PDOException$e){
echo'Hasurgidounerrorynosepuedeconectaralabasede
datos.Detalle:'.$e>getMessage();
exit;
}
}
}
?>

Y luego crear una segunda clase que se encargar de todo lo referido con
consultas con la tabla de personajes que se llame Personaje, en la misma
carpeta de la clase Conexion. Pero antes de mostrarles el cdigo paremos aqu.

Primero pensemos en qu propiedades va a necesitar nuestra clase Personaje.


Por cada registro necesitar guardar el id, el nombre y la descripcin del
personaje, pero tambin para realizar consultas necesitar a qu tabla
apuntar. As que crearemos tres propiedades para guardar los datos de cada
personaje y una constante donde guardar el nombre de la tabla. Probemos de
esta forma:

<?php
require_once'Conexion.php';
classPersonaje{
private$id;
private$nombre;
private$descripcion;
constTABLA='personaje';
publicfunction__construct($nombre,$descipcion,$id=null){
$this>nombre=$nombre;
$this>descripcion=$descipcion;
$this>id=$id;
}
}
?>

Ok, echemos un vistazo a esta clase. En primer lugar, import la


clase Conexion, ya que ms adelante tendr que hacer consultas a la base y
necesitar mucho de la misma. Luego cre tres propiedades para guardar los
datos de cada personaje y una constante con el nombre de la tabla. Y por
ltimo cre un constructor donde tendr que setear el valor de las propiedades.
Si se han fijado bien, el ltimo parmetro, $id, es un parmetro que por defecto
es null, esto significa que en el momento de crear el constructor si yo no
ingreso ese ltimo parmetro tomar ese valor, nulo. Esto se debe a que no
siempre el id tendr un valor definido, por ejemplo, si yo estoy creando un
nuevo usuario no ingresar el valor del id ya que eso ser auto incrementable y
el valor lo recuperar una vez insertado el registro.

Continuemos, necesitaremos un mtodo que nos permita insertar y modificar


datos en la tabla personaje, para ello crearemos un mtodo
llamado guardar(), que nos servir para insertar y modificar registros. Para ello
crearemos un objeto de la clase Conexion que por medio de los mtodos que
hered de la clase PDO podr realizar esto. La clasePersonaje ahora quedara
as:

<?php
require_once'Conexion.php';
classPersonaje{
private$id;
private$nombre;
private$descripcion;
constTABLA='personaje';
publicfunctiongetId(){
return$this>id;
}
publicfunctiongetNombre(){
return$this>nombre;
}
publicfunctiongetDescripcion(){
return$this>descripcion;
}
publicfunctionsetNombre($nombre){
$this>nombre=$nombre;
}
publicfunctionsetDescripcion($descripcion){
$this>descripcion=$descripcion;
}
publicfunction__construct($nombre,$descipcion,$id=null){
$this>nombre=$nombre;
$this>descripcion=$descipcion;
$this>id=$id;
}
publicfunctionguardar(){
$conexion=newConexion();
if($this>id)/*Modifica*/{
$consulta=$conexion>prepare('UPDATE'.self::TABLA.'SET
nombre=:nombre,descripcion=:descripcionWHEREid=:id');
$consulta>bindParam(':nombre',$this>nombre);
$consulta>bindParam(':descripcion',$this>descripcion);
$consulta>bindParam(':id',$this>id);
$consulta>execute();
}else/*Inserta*/{
$consulta=$conexion>prepare('INSERTINTO'.self::TABLA.'
(nombre,descripcion)VALUES(:nombre,:descripcion)');
$consulta>bindParam(':nombre',$this>nombre);
$consulta>bindParam(':descripcion',$this>descripcion);
$consulta>execute();
$this>id=$conexion>lastInsertId();
}
$conexion=null;
}
}
?>

Ok, analicemos los cambios. En primer lugar agregu los mtodos getters y
setters que me estaban faltando, luego cre un mtodo guardar() que primero
crea un objeto Conexion, para as luego por medio de ese objeto poder
conectarse a la base de datos:

$conexion=newConexion();

Luego pregunt por el valor del id mediante un if, si el valor es cualquier cosa
que no sea 0, false o null entonces entrar en el lado verdadero del
condicional. Esto sirve para indicarle a nuestro mtodo si tendr que hacer
un update o un insert, si el valor del id esalgo significa existe un id, por ende
se est modificar un usuario con ese valor, de lo contrario se est intentando
insertar un usuario nuevo.

Analicemos el lado verdadero del condicional, osea el update:

Primero escribimos la consulta que modificar los datos del usuario:

$consulta=$conexion>prepare('UPDATE'.self::TABLA.'SETnombre=
:nombre,descripcion=:descripcionWHEREid=:id');

Como ven creamos un nuevo objeto $consulta, que nos devuelve el


mtodo prepare(). Aqu es donde se define la consulta que se est haciendo
en la base.

Luego por medio del mtodo bindParam() agregamos los valores de la


consulta, por ejemplo :nombre ser el valor de la propiedad $nombre:

$consulta>bindParam(':nombre',$this>nombre);
$consulta>bindParam(':descripcion',$this>descripcion);
$consulta>bindParam(':id',$this>id);

Y finalmente ejecutamos la consulta:

$consulta>execute();

El lado falso del if, osea el que sirve para insertar un nuevo registro es muy
parecido al del update, slo cambia la consulta (obviamente) y que no debe
pasarle el valor del id, porque como dijimos antes, en el INSERT no hay id, sino
que se auto incrementa en la consulta.

Por eso al finalizar de ejecutar la consulta se recupera el id que se acaba de


insertar:

$this>id=$conexion>lastInsertId();

El mtodo lastInsertId() me permite recuperar la ltima clave primaria o id


insertada en la base de datos, es un mtodo de la clase PDO.

Finalmente igualo el objeto $conexion a null para cerrar la conexin:

$conexion=null;

Ahora para comprobar que todo ha salido bien probaremos crear un


objeto Personajepara insertar un registro en la base de datos:

<?php
require_once'clases/Personaje.php';
$persona=newPersonaje('Batman','Encapuchado,disfrazadodemurcilago
quesaleporlasnochesacombatirelmal.',1);
$persona>guardar();
echo$persona>getNombre().'sehaguardadocorrectamenteconelid:
'.$persona>getId();
?>

Al no ingresarle en el constructor el tercer parmetro no obligatorio $id el


mtodoguardar() interpretar que se est intentado insertar un nuevo registro.
Prueben ejecutar este script y vern que se ha insertado un nuevo registro en la
tabla personaje.

En la prxima ocasin veremos cmo hacer select en la base de datos.

Saludos!

Anterior: Php orientado a objetos, parte 10: PDO, Conectarse a una base de
datos
Siguiente: Php orientado a objetos, parte 12: PDO, Buscar registros en una base
de datos
Consultas a bases de datos desde PHP
con PDO
por Jose el 01/07/2012
Si el desarrollo de tus aplicaciones lo haces con PHP usando como base de datos
MySql y no utilizas ningn tipo de framework, seguramente las consultas a la base
de datos las realizars con el API de mysql. Un ejemplo del uso de este API es el
siguiente:
?
1
$con = mysql_connect('localhost', 'user', pass');
2
3 mysql_select_db('nombreBaseDatos');
4 $sql = 'SELECT * FROM tabla';
5
6 $res = mysql_query($sql);
7
8 while( $row = mysql_fetch_array($res) )
echo $row['titulo'];
9
10 mysql_free_result($res);
11 mysql_close($con);
12
Si este es tu caso, deberas saber que ests usando una API obsoleta y
desaconsejada por el equipo de PHP. En su lugar deberas usar el API de mysqli o
mejor an PDO (PHP Data Objects). Utilizando PDO podemos solventar muchas
dificultades que surgen al utilizar la API de mysql. Un par de ventajas que se
obtienen con PDO es que el proceso de escapado de los parmetros es sumamente
sencillo y sin la necesidad de estar atentos a utilizar en todos los casos funciones
para este cometido como mysql_real_escape_string(). Otra ventaja es que PDO es
una API flexible y nos permite trabajar con cualquier tipo de base de datos y no
estar restringidos a utilizar MySql.
Veamos como podemos utilizar PDO en una aplicacin para recuperar datos de una
base de datos MySql y ver lo sencillo que es usar esta API. Lo primero que tenemos
que hacer es realizar la conexin a la base de datos. La conexin la realizaremos
con el constructor de la clase de la siguiente forma:
?
1 $con = new PDO('mysql:host=localhost;dbname=nombreBaseDatos', 'user', 'pass');
Con la llamada anterior ya tenemos creada la conexin a la base de datos. Antes de
continuar voy a explicar como tratar los posibles errores con PDO. Por defecto PDO
viene configurado para no mostrar ningn error. Es decir que para saber si se ha
producido un error, tendramos que estar comprobando los
mtodos errorCode() y errorInfo(). Para facilitarnos la tarea vamos a habilitar
las excepciones. De esta forma cada vez que ocurra un error saltar una excepcin
que capturaremos y podremos tratar correctamente para mostrarle un mensaje al
usuario. Para realizar esta tarea utilizaremos la funcin setAttribute() de la
siguiente forma:
?
1 $con = new PDO('mysql:host=localhost;dbname=nombreBaseDatos', 'user', 'pass');
2 $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Los posibles valores que se le podra asignar a ATTR_ERRMODE son:

PDO::ERRMODE_SILENT es el valor por defecto y como he mencionado


antes no lanza ningn tipo de error ni excepcin, es tarea del programador
comprobar si ha ocurrido algn error despus de cada operacin con la base
de datos.
PDO::ERRMODE_WARNING genera un error E_WARNING de PHP si ocurre
algn error. Este error es el mismo que se muestra usando la API de mysql
mostrando por pantalla una descripcin del error que ha ocurrido.
PDO::ERRMODE_EXCEPTION es el que acabamos de explicar que genera
y lanza una excepcin si ocurre algn tipo de error.

Como acabamos de hacer que se lancen excepciones cuando se produzca algn


error, el paso que tenemos que dar a continuacin es capturarlas por si se
producen, para ello realizamos lo siguiente:

?
1
try
2 {
3 $con = new PDO('mysql:host=localhost;dbname=nombreBaseDatos', 'user', 'pass');
4 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
5 }
6 catch(PDOException $e)
{
7 echo 'Error conectando con la base de datos: ' . $e->getMessage();
8 }
9
Ahora que sabemos como conectarnos a la base de datos, vamos a crear una
sentencia para poder recuperar datos. Para ejecutar sentencias podemos utilizar la
llamada a query() o bien la llamada a prepare(). Aunque tenemos disponibles las
dos llamadas es mucho ms seguro utilizar la llamada a prepare() ya que esta se
encarga de escapar por nosotros los parmetros y nos asegura que no sufriremos
problemas de SQL Injection. La funcin query() se suele utilizar cuando la
sentencia que vamos a ejecutar no contiene parmetros que ha enviado el usuario.
Veamos un ejemplo utilizando la funcin query():
?
1
2 try
3 {
$con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
4 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
5
6 $datos = $con->query('SELECT nombre FROM personal');
7 foreach($datos as $row)
8 echo $row[0] . '<br/>';
}
9 catch(PDOException $e)
10 {
11 echo 'Error conectando con la base de datos: ' . $e->getMessage();
12 }
13
Si a pesar de las advertencias aun quieres ejecutar sentencias
con query() pasndole parmetros de usuarios, la forma correcta de hacerlo sera
escapando esos parmetros con la funcin quote()como se muestra a
continuacin:
?
1
$ape = 'Hernandez';
2
3 try
4 {
5 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
6 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
7
8 $datos = $con->query('SELECT nombre FROM personal WHERE apellidos like ' . $con-
>quote($ape));
9 foreach($datos as $row)
10 echo $row[0] . '<br/>';
11 }
12 catch(PDOException $e)
{
13 echo 'Error conectando con la base de datos: ' . $e->getMessage();
14 }
15

La forma de utilizar la funcin prepare() que es la ms recomendada es la


siguiente:

?
1 try
{
2 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
3 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
4
5 $stmt = $con->prepare('SELECT nombre FROM personal');
6 $stmt->execute();
7
8 while( $datos = $stmt->fetch() )
echo $datos[0] . '<br/>';
9 }
10 catch(PDOException $e)
11 {
12 echo 'Error: ' . $e->getMessage();
13 }
14
15
Como se ve, es realmente simple ejecutar consultas. Simplemente tenemos que
indicarle a la funcin prepare() la sentencia sql que queremos ejecutar. Esta
funcin nos devolver unPDOStatement sobre el cual ejecutaremos la
funcin fetch() para poder mostrar su valor.

Si necesitamos pasarle valores a la sentencia sql, utilizaramos los parmetros. Los


parmetros los indicamos en la misma sentencia sql y los podemos escribir de dos
formas distintas. Mediante el signo ? o mediante un nombre de variable precedido
por el simbolo : :nombreParam. La segunda forma nos permite una identificacin
ms fcil de los parmetros, pero cualquiera de las dos formas es correcta. Veamos
un ejemplo:

?
1
$ape = 'Hernandez';
2
3
try
4 {
5 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
6 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
7
8 $stmt = $con->prepare('SELECT nombre FROM personal WHERE apellidos like
:apellidos');
9 $stmt->execute(array(':apellidos' => $ape ));
10
11 while( $datos = $stmt->fetch() )
12 echo $datos[0] . '<br />';
13 }
14 catch(PDOException $e)
{
15 echo 'Error: ' . $e->getMessage();
16 }
17
Como se ve hemos llamado al parmetro :apellidos y posteriormente en la llamada
a la funcinexecute() indicamos con un array asociativo el nombre del parmetro
y su valor. Otra forma de indicar los parmetros es utilizando la
funcin bindParam.
?
1 $ape = 'Hernandez';
2
try
3 {
4 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
5 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
6
7 $stmt = $con->prepare('SELECT nombre FROM personal WHERE apellidos like
8 :apellidos');
$stmt->bindParam(':apellidos', $ape, PDO::PARAM_INT);
9 $stmt->execute();
10
11 while( $datos = $stmt->fetch() )
12 echo $datos[0] . '<br />';
13 }
14 catch(PDOException $e)
{
15 echo 'Error: ' . $e->getMessage();
16
17 }
18
A la funcin bindParam() le pasamos el nombre del parmetro, su valor y
finalmente el tipo que es. Los tipos de parmetros que le podemos pasar los
podemos ver en las constantes predefinidas de PDO y son:
PDO::PARAM_BOOL
PDO::PARAM_NULL
PDO::PARAM_INT
PDO::PARAM_STR
PDO::PARAM_LOB

Al igual que las sentencias select, podemos utilizar las


funciones query() y prepare() para ejecutar inserts, updates y deletes. La forma
de hacerlo es igual que lo que hemos estado viendo hasta ahora:

Ejemplo de insert:

?
1
2 $nom = 'Jose';
$ape = 'Hernandez';
3
4 try
5 {
6 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
7 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
8
$stmt = $con->prepare('INSERT INTO personal (nombre, apellidos) VALUES (:nombre
9
:apellidos)');
10
$rows = $stmt->execute( array( ':nombre' => $nom,
11 ':apellidos' => $ape));
12
13 if( $rows == 1 )
14 echo 'Insercin correcta';
15 }
catch(PDOException $e)
16 {
17 echo 'Error: ' . $e->getMessage();
18 }
19

Ejemplo de update:

?
1 $nom = 'Jose';
$ape = 'Hernandez';
2
3
try
4 {
5 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
6 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
7
8 $stmt = $con->prepare('UPDATE personal SET apellidos = :apellidos WHERE nombre
:nombre');
9
$rows = $stmt->execute( array( ':nombre' => $nom,
10
11
':apellidos' => $ape));
12 if( $rows > 0 )
13 echo 'Actualizacin correcta';
14 }
15 catch(PDOException $e)
{
16 echo 'Error: ' . $e->getMessage();
17 }
18

Ejemplo de delete:

?
1
2 $ape = 'Hernandez';
3
4 try
5 {
$con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
6 $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
7
8 $stmt = $con->prepare('DELETE FROM personal WHERE apellidos = :apellidos');
9 $rows = $stmt->execute( array( ':apellidos' => $ape));
10
11 if( $rows > 0 )
echo 'Borrado correcto';
12
}
13 catch(PDOException $e)
14 {
15 echo 'Error: ' . $e->getMessage();
16 }
17

Para acabar con esta entrada sobre PDO vamos a ver otra de las funcionalidades
que nos aporta y que puede ser muy til. PDO nos permite realizar consultas y
mapear los resultados en objetos de nuestro modelo. Para ello primero tenemos
que crearnos una clase con nuestro modelo de datos.

?
1
class Usuario
2 {
3 private $nombre;
4 private $apellidos;
5
6 public function nombreApellidos()
7 {
return $this->nombre . ' ' . $this->apellidos;
8 }
9 }
10

Hay que tener en cuenta que para que funcione correctamente, el nombre de los
atributos en nuestra clase tienen que ser iguales que los que tienen las columnas
en nuestra tabla de la base de datos. Con esto claro vamos a realizar la consulta.

?
1
2 try
3 {
4 $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
5
6 $stmt= $con->prepare('SELECT nombre, apellidos FROM personal');
7 $stmt->execute();
8
9 $stmt->setFetchMode(PDO::FETCH_CLASS, 'Usuario');
10
11 while($usuario = $stmt->fetch())
echo $usuario->nombreApellidos() . '<br>';
12
13 }
14 catch(PDOException $e)
15 {
16 echo 'Error: ' . $e->getMessage();
17 }
18
La novedad que podemos ver en este script es la llamada al
mtodo setFetchMode() pasndole como primer argumento la constante
PDO::FETCH_CLASS que le indica que haga un mapeado en la clase que le
indicamos como segundo argumento, en este caso la clase Usuario que hemos
creado anteriormente. Despus al recorrer los elementos con fetch los resultados
en vez de en un vector los obtendremos en el objeto indicado.

Despus de todo esto solo me queda decir que si eres de los que todava sigues
usando la antigua API de mysql este es un buen momento para empezar a cambiar
y a usar una nueva API ms moderna y con mejores prestaciones.

PHP Data Objects (PDO)


La extensin 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 ms populares (MySQL, Oracle, MS SQL Server, PostgreSQL,
SQLite, Firebird, DB2, Informix, etc).

En esta leccin se explica el acceso a MySQL y SQLite mediante


PDO. La extensin PDO no evala la correcin de las consultas SQL.

Conexin con la base de datos

o Conexin con MySQL

o Conexin con SQLite 3

o Conexin configurable
Desconexin con la base de datos

Consultas a la base de datos

Seguridad en las consultas: consultas preparadas

o Consultas preparadas

o Restricciones en los parmetros de consultas preparadas

Ejemplos de consultas

o Consultas CREATE DATABASE, DROP DATABASE,


CREATE TABLE

Consultas en MySQL

Consultas en SQLite

Solucin configurable

o Consultas DROP TABLE, INSERT INTO, UPDATE,


DELETE FROM

o Consulta SELECT

o Consulta SELECT LIKE

o Consultas de unin de tablas

Conexin 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
pgina php que incluya consultas a la base de datos es necesario
conectar primero con la base de datos.

Si no se puede establecer la conexin 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 extensin pdo
o (en el caso de SQLite) que no exista el camino donde se guarda la
base de datos.
Conexin con MySQL

En el caso de MySQL, la creacin de la clase PDO incluye el nombre


del servidor, el nombre de usuario y la contrasea.

Para poder acceder a MySQL mediante PDO, debe estar activada la


extensin php_pdo_mysql en el archivo de configuracin php.ini
(vase el apartado extensin pdo_mysql en la leccin de configuracin
de Apache y PHP).
// FUNCIN DE CONEXIN CON LA BASE DE DATOS MYSQL
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();
}
}

// EJEMPLO DE USO DE LA FUNCIN ANTERIOR


// La conexin se debe realizar en cada pgina que acceda a la
base de datos
$db = conectaDB();

Conexin con SQLite 3

En SQLite, no se hace una conexin 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


extensin php_pdo_sqlite en el archivo de configuracin php.ini (vase
el apartado extensin pdo_sqlite en la leccin de configuracin de
Apache y PHP).
// FUNCIN DE CONEXIN CON LA BASE DE DATOS SQLITE
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();
}
}

// EJEMPLO DE USO DE LA FUNCIN ANTERIOR


// La conexin se debe realizar en cada pgina 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 cambiarlo
a otro.

Conexin configurable

Si se incluyen ambas conexiones en el mismo programa, cada usuario


puede elegir la base de datos ms conveniente en cada caso.

En los ejercicios en este curso se propone al alumno organizar los


programas de manera que puedan trabajar tanto con SQLite como con
MySQL y hacerlo de forma organizada, para que se puedan aadir
fcilmente otras bases de datos.

Para ello se crearn dos bibliotecas, una dedicada a MySQL y otra a


SQLite, que contengan las funciones especficas de cada base de
datos. Adems habr una biblioteca general en la que se pueda
seleccionar la biblioteca a utilizar (MySQL o SQLite). As, cada pgina
llamar a la biblioteca general y esta llamar a la biblioteca especfica.

Por ejemplo, para el caso de la funcin de conexin, el resultado sera:

en cualquier fichero que quiera conectar con la base de datos se


llamara a la biblioteca general y se llamara a la funcin
genrica conectaDB():

// EJEMPLO DE USO DE CONEXIN CONFIGURABLE


// La conexin se debe realizar en cada pgina que acceda a
la base de datos
require_once "biblioteca.php";
$db = conectaDB();

biblioteca.php: en ella se selecciona la biblioteca especfica a


cargar:

// biblioteca.php
define("MYSQL", "MySQL"); // Base de datos
MySQL
define("SQLITE", "SQLite"); // Base de datos
SQLITE

$dbMotor = SQLITE; // Base de datos
empleada (MYSQL o SQLITE)

if ($dbMotor == MYSQL) {
require_once "biblioteca_mysql.php";
} elseif ($dbMotor == SQLITE) {
require_once "biblioteca_sqlite.php";
}

biblioteca_mysql.php: contiene la definicin de la funcin


conectaDB() especfica para trabajar con MySQL

// biblioteca_mysql.php
function conectaDb()
{
try {
$db = new PDO(MYSQL_HOST, MYSQL_USUARIO,
MYSQL_PASSWORD);
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,
true);
return($db);
} catch(PDOException $e) {
cabecera("Error grave", MENU_PRINCIPAL);
print "<p>Error: No puede conectarse con la base de
datos.</p>\n";
print "<p>Error: " . $e->getMessage() . "</p>\n";
pie();
exit();
}
}

biblioteca_sqlite.php: contiene la definicin de la funcin


conectaDB() especfica para trabajar con SQLite

// biblioteca_sqlite.php
function conectaDb()
{
global $dbDb;

try {
$db = new PDO("sqlite:" . $dbDb);
return($db);
} catch(PDOException $e) {
cabecera("Error grave", MENU_PRINCIPAL);
print "<p>Error: No puede conectarse con la base de
datos.</p>\n";
print "<p>Error: " . $e->getMessage() . "</p>\n";
pie();
exit();
}
}

Volver al principio de la pgina

Desconexin 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
pgina.
$db = null;

Volver al principio de la pgina


Consultas a la base de datos

Una vez realizada la conexin a la base de datos, las operaciones se


realizan a travs de consultas.

El mtodo 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.

Si es una consulta que no devuelve registros, sino que


simplemente realiza una accin que puede tener xito o no (por
ejemplo, insertar un registro), el mtodo
devuelve true o false. No es necesario guardar el resultado
de la consulta en ninguna variable, pero se puede utilizar para
sacar un mensaje diciendo que todo ha ido bien (o no). Por
ejemplo,

// EJEMPLO DE CONSULTA DE INSERCIN DE REGISTRO


require_once "biblioteca.php";
$db = conectaDB();

$consulta = "INSERT INTO $dbTabla
(nombre, apellidos)
VALUES ("$nombre", "$apellidos")";
if ($db->query($consulta)) {
print "<p>Registro creado correctamente.</p>\n";
} else {
print "<p>Error al crear el registro.<p>\n";
}

$db = null;

Pero si la consulta devuelve registros, el mtodo devuelve los


registros correspondientes o false. En ese caso s que es
conveniente guardar lo que devuelve el mtodo en una variable
para procesarla posteriormente. Si contiene registros, la variable
es de un tipo especial llamado recurso que no se puede acceder
directamente, pero que se puede recorrer con un
bucle foreach(),

// EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS


require_once "biblioteca.php";
$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";
}
}

$db = null;

En los ejemplos, se define una variable $consulta que contiene la


consulta y a continuacin se ejecuta la consulta, pero podra estar en
una sola:
// En dos lneas
$consulta = "SELECT * FROM $dbTabla";
$result = $db->query($consulta);

// En una sola lnea


$result = $db->query("SELECT * FROM $dbTabla");

Se recomienda utilizar la primera versin, que permite por ejemplo


imprimir la consulta mientras se est programando para comprobar
que no tiene errores:
$consulta = "SELECT * FROM $dbTabla";
print "<p>Consulta: $consulta</p>\n";
$result = $db->query($consulta);

Volver al principio de la pgina

Seguridad en las consultas: consultas preparadas

Para evitar ataques de inyeccin SQL (en la leccin Inyecciones


SQL se comentan los ataques ms elementales), se recomienda el
uso de sentencias preparadas, en las que PHP se encarga de
"desinfectar" los datos en caso necesario. En general, cualquier
consulta que incluya datos introducidos por el usuario debe realizarse
mediante consultas preparadas.

Consultas preparadas

El mtodo para efectuar consultas es primero preparar la consulta


con PDO->prepare($consulta) y despus ejecutarla con PDO-
>execute(array(parmetros)), que devuelve el resultado de la
consulta.
// Consulta preparada
$consulta = "SELECT * FROM $dbTabla";
$result = $db->prepare($consulta);
$result->execute();

Dependiendo del tipo de consulta, el dato devuelto debe tratarse de


formas distintas, como se ha explicado en el apartado anterior.

Si la consulta incluye datos introducidos por el usuario, los datos


pueden incluirse directamente en la consulta, pero en ese caso, PHP
no realiza ninguna "desinfeccin" de los datos, por lo que estaramos
corriendo riesgos de ataques:
$nombre = $_REQUEST["nombre"];
$apellidos = $_REQUEST["apellidos"];

$consulta = "SELECT COUNT(*) FROM $dbTabla


WHERE nombre=$nombre
AND apellidos=$apellidos"; // DESACONSEJADO:
PHP NO DESINFECTA LOS DATOS
$result = $db->prepare($consulta);
$result->execute();
if (!$result) {
print "<p>Error en la consulta.</p>\n";
...

Para que PHP desinfecte los datos, estos deben enviarse al ejecutar
la consulta, no al prepararla. Para ello es necesario indicar en la
consulta la posicin de los datos. Esto se puede hacer de dos
maneras, mediante parmetros o mediante interrogantes, aunque se
aconseja la utilizacin de parmetros:

mediante parmetros (:parametro)

En este caso la matriz debe incluir los nombres de los


parmetros y los valores que sustituyen a los parmetros (el
orden no es importante), como muestra el siguiente ejemplo:
$nombre = $_REQUEST["nombre"];
$apellidos = $_REQUEST["apellidos"];

$consulta = "SELECT COUNT(*) FROM $dbTabla


WHERE nombre=:nombre
AND apellidos=:apellidos";
$result = $db->prepare($consulta);
$result->execute(array(":nombre" => $nombre, ":apellidos" =>
$apellidos));
if (!$result) {
print "<p>Error en la consulta.</p>\n";
...

mediantes interrogantes (?)

En este caso la matriz debe incluir los valores que sustituyen a


los interrogantes en el mismo orden en que aparecen en la
consulta, como muestra el siguiente ejemplo:
$nombre = $_REQUEST["nombre"];
$apellidos = $_REQUEST["apellidos"];

$consulta = "SELECT COUNT(*) FROM $dbTabla


WHERE nombre=?
AND apellidos=?";
$result = $db->prepare($consulta);
$result->execute(array($nombre, $apellidos));
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 caracteres especiales del html, etc.,
como se comenta en la leccin de Recogida de datos.
Restricciones en los parmetros de consultas preparadas

Debido a que las consultas preparadas se idearon para optimizar el


rendimiento de las consultas, el uso de parmetros tiene algunas
restricciones. Por ejemplo

los identificadores (nombres de tablas, nombres de columnas,


etc) no pueden sustituirse por parmetros

los dos elementos de una igualdad no pueden sustituirse por


parmetros

en general no pueden utilizarse partros en las consultas DDL


(lenguaje de definicin de datos) (nombre y tamao de los
campos, etc.)

Si no podemos usar parmetros, no queda ms remedio que incluir los


datos en la consulta. Como en ese caso PHP no hace ninguna
desinfeccin de los datos, la tenemos que hacer nosotros
previamente.

Como en estos casos los valores introducidos por el usuario suelen


tener unos valores restringidos (por ejemplo, si el usuario puede elegir
una columna de una tabla, los nombres de las columnas estn
determinadas y el usuario slo puede elegir uno de ellos). Podemos
crear una funcin de recogida de datos especfica que impida
cualquier tipo de ataque de inyeccin por parte del usuario, como
muestra el siguiente ejemplo
// FUNCIN DE RECOGIDA DE UN DATO QUE SLO PUEDE TOMAR
DETERMINADOS VALORES
$campos = array(
"nombre",
"apellidos");

function recogeCampo($var, $var2)


{
global $campos;

foreach($campos as $campo) {
if (isset($_REQUEST[$var]) && $_REQUEST[$var] == $campo) {
return $campo;
}
}
return $var2;
}

// EJEMPLO DE USO DE LA FUNCIN ANTERIOR


$campo = recogeCampo("campo", "apellidos");
$nombre = $_REQUEST["nombre"];

$consulta = "SELECT * FROM $dbTabla


WHERE nombre=:nombre
ORDER BY $campo ASC";
$result = $db->prepare($consulta);
$result->execute(array(":nombre" => $nombre));
if (!$result) {
print "<p>Error en la consulta.</p>\n";
...

Volver al principio de la pgina

Ejemplos de consultas

En los ejemplos de este apartado, se han utilizado sentencias


preparadas en los casos en los que las consultas incluyen datos
proporcionados por el usuario y consultas no preparadas cuando no
incluyan datos proporcionados por el usuario. En la mayora de los
casos se podran haber gastado sentencias preparadas aunque no
haya datos proporcionados por el usuario.

Volver al principio de la pgina

Consultas CREATE DATABASE, DROP DATABASE, CREATE TABLE

Estas consultas no son iguales en MySQL y SQLite. En los ejercicios


propuestos para que se pueda utilizar una u otra base de datos, estas
consultas se incluyen en las bibliotecas especficas.

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 tabla, se utiliza la consulta CREATE TABLE. Las
consultas de creacin de tabla suelen ser especficas de cada
base de datos. Los ejemplos no utilizan sentencias preparadas
(en caso de utilizarse sentencias preparadas, las variables no
podran ir como parmetros por tratarse de sentencias DDL).

Consultas en MySQL

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


DATABASE.
// EJEMPLO DE CONSULTA DE CREACIN DE BASE DE DATOS EN MYSQL
$consulta = "CREATE DATABASE $dbDb
CHARACTER SET latin1 COLLATE latin1_spanish_ci";
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";
}

Nota: El juego de carcteres utilizado en este curso es iso-8859-1, por


lo que en la base de datos MySQL se utiliza el juego de
caracteres latin1 y el cotejamiento latin1_spanish_ci.

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


DATABASE.
// EJEMPLO DE CONSULTA DE BORRADO DE BASE DE DATOS EN MYSQL
$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";
}

Para crear una tabla, se utiliza la consulta CREATE TABLE. Las


consultas de creacin de tabla suelen ser especficas de cada base de
datos. El ejemplo no utiliza sentencias preparadas (en caso de
utilizarse sentencias preparadas, las variables no podran ir como
parmetros por tratarse de sentencias DDL).
// EJEMPLO DE CONSULTA DE CREACIN DE TABLA EN MYSQL
$consulta = "CREATE TABLE $dbTabla (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
nombre VARCHAR($tamNombre),
apellidos VARCHAR($tamApellidos),
PRIMARY KEY(id)
)";
if ($db->query($consulta)) {
print "<p>Tabla creada correctamente.</p>\n";
} else {
print "<p>Error al crear la tabla.</p>\n";
}

Consultas en SQLite

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. Es suficiente borrar y crear las
tablas.
// EJEMPLO DE CONSULTA DE BORRADO DE TABLA EN SQLITE
$consulta = "DROP TABLE $dbTabla";
if ($db->query($consulta)) {
print "<p>Tabla borrada correctamente.</p>\n";
} else {
print "<p>Error al borrar la tabla.</p>\n";
}

Para crear una tabla, se utiliza la consulta CREATE TABLE. Las


consultas de creacin de tabla suelen ser especficas de cada base de
datos. El ejemplo no utiliza sentencias preparadas (en caso de
utilizarse sentencias preparadas, las variables no podran ir como
parmetros por tratarse de sentencias DDL).
// EJEMPLO DE CONSULTA DE CREACIN DE TABLA EN SQLite
$consulta = "CREATE TABLE $dbTabla (
id INTEGER PRIMARY KEY,
nombre VARCHAR($tamNombre),
apellidos VARCHAR($tamApellidos)
)";
if ($db->query($consulta)) {
print "<p>Tabla creada correctamente.</p>\n";
} else {
print "<p>Error al crear la tabla.</p>\n";
}
Solucin configurable

El resultado sera

en el fichero que quiera reiniciar la base de datos se llamara a


la biblioteca general y se llamara a la funcin genrica
borraTodo():

// EJEMPLO DE USO DE CONEXIN CONFIGURABLE// La conexin se


debe realizar en cada pgina que acceda a la base de datos
require_once "biblioteca.php";
$db = conectaDb();
borraTodo($db);
$db = null;

biblioteca_mysql.php: contiene la definicin de la funcin


borraTodo() especfica para trabajar con MySQL y que borra la
base de datos, la crea y crea la tabla:

// biblioteca_mysql.php
$consultaCreaTabla = "CREATE TABLE $dbTabla (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
nombre VARCHAR($tamNombre),
apellidos VARCHAR($tamApellidos),
PRIMARY KEY(id)
)";

function borraTodo($db)
{
global $dbDb, $consultaCreaTabla;

$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";
}
$consulta = "CREATE DATABASE $dbDb";
if ($db->query($consulta)) {
print "<p>Base de datos creada correctamente.</p>\n";
$consulta = $consultaCreaTabla;
if ($db->query($consulta)) {
print "<p>Tabla creada correctamente.</p>\n";
} else {
print "<p>Error al crear la tabla.</p>\n";
}
} else {
print "<p>Error al crear la base de datos.</p>\n";
}
}

biblioteca_sqlite.php: contiene la definicin de la funcin


borraTodo() especfica para trabajar con SQLite y que borra la
tabla y la crea:

// biblioteca_sqlite.php
$consultaCreaTabla = "CREATE TABLE $dbTabla (
id INTEGER PRIMARY KEY,
nombre VARCHAR($tamNombre),
apellidos VARCHAR($tamApellidos)
)";

function borraTodo($db)
{
global $dbTabla, $consultaCreaTabla;

$consulta = "DROP TABLE $dbTabla";
if ($db->query($consulta)) {
print "<p>Tabla borrada correctamente.</p>\n";
} else {
print "<p>Error al borrar la tabla.</p>\n";
}
$consulta = $consultaCreaTabla;
if ($db->query($consulta)) {
print "<p>Tabla creada correctamente.</p>\n";
} else {
print "<p>Error al crear la tabla.</p>\n";
}
}

Volver al principio de la pgina


Consultas DROP TABLE, INSERT INTO, UPDATE, DELETE FROM

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


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

Para aadir un registro a una tabla, se utiliza la consulta INSERT


INTO.
// EJEMPLO DE CONSULTA DE INSERCIN DE REGISTRO
$nombre = recoge("nombre");
$apellidos = recoge("apellidos");

$consulta = "INSERT INTO $dbTabla


(nombre, apellidos)
VALUES (:nombre, :apellidos)";
$result = $db->prepare($consulta);
if ($result->execute(array(":nombre" => $nombre, ":apellidos" =>
$apellidos))) {
print "<p>Registro creado correctamente.</p>\n";
} else {
print "<p>Error al crear el registro.</p>\n";
}

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


// EJEMPLO DE CONSULTA DE MODIFICACIN DE REGISTRO
$nombre = recoge("nombre");
$apellidos = recoge("apellidos");
$id = recoge("id");

$consulta = "UPDATE $dbTabla


SET nombre=:nombre, apellidos=:apellidos
WHERE id=:id";
$result = $db->prepare($consulta);
if ($result->execute(array(":nombre" => $nombre, ":apellidos" =>
$apellidos, ":id" => $id))) {
print "<p>Registro modificado correctamente.</p>\n";
} else {
print "<p>Error al modificar el registro.</p>\n";
}

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


FROM.

Nota: En el ejemplo, los registros a borrar se reciben en forma de


matriz y se recorre la matriz borrando un elemento en cada iteracin.
// EJEMPLO DE CONSULTA DE BORRADO DE REGISTRO
$id = recogeMatriz("id");

foreach ($id as $indice => $valor) {


$consulta = "DELETE FROM $dbTabla
WHERE id=:indice";
$result = $db->prepare($consulta);
if ($result->execute(array(":indice" => $indice))) {
print "<p>Registro borrado correctamente.</p>\n";
} else {
print "<p>Error al borrar el registro.</p>\n";
}
}

Volver al principio de la pgina

Consulta SELECT

Para obtener registros que cumplan determinados criterios se utiliza


una consulta SELECT.

Si se produce un error en la consulta, la consulta devuelve el


valor false .

// EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS


$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";
}
Si la consulta devuelve un nico registro se puede utilizar la
funcin PDOStatement->fetchColumn() para recuperar la
primera columna.

// EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS


$consulta = "SELECT COUNT(*) FROM $dbTabla";
$result = $db->query($consulta);
if (!$result) {
print "<p>Error en la consulta.</p>\n";
} else {
$encontrados = $result->fetchColumn();
print "<p>Se han encontrado $encontrados
registros.</p>\n";
}

Si la consulta se ejecuta correctamente, la consulta devuelve los


registros correspondientes.

o Para acceder a los registros devueltos por la consulta, se


puede utilizar un bucle foreach.
o // EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS
o $consulta = "SELECT * FROM $dbTabla";
o $result = $db->query($consulta);
o if (!$result) {
o print "<p>Error en la consulta.</p>\n";
o } else {
o foreach ($result as $valor) {
o print "<p>Nombre: $valor[nombre] - Apellidos:
$valor[apellidos]</p>\n";
o }
}

o O tambin se puede utilizar la funcin PDOStatement-


>fetch().
o // EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS
o $consulta = "SELECT * FROM $dbTabla";
o $result = $db->query($consulta);
o if (!$result) {
o print "<p>Error en la consulta.</p>\n";
o } else {
o while ($valor = $result->fetch()) {
o print "<p>Nombre: $valor[nombre] - Apellidos:
$valor[apellidos]</p>\n";
o }
}

Si la consulta no devuelve ningn registro, los dos bucles


anteriores (foreach o fetch) no escribiran nada. Por ello se
recomienda hacer primero una consulta que cuente el nmero
de resultados de la consulta y, si es mayor que cero, hacer la
consulta.

El ejemplo siguiente utiliza la funcin PDOStatement-


>fetchColumn(), que devuelve la primera columna del primer
resultado (que en este caso contiene el nmero de registros de
la consulta).
// EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS
$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 todava ningn registro en la
tabla.</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 "<p>Nombre: $valor[nombre] - Apellidos:
$valor[apellidos]</p>\n";
}
}
}

Volver al principio de la pgina

Consulta SELECT LIKE

La consulta SELECT permite efectuar bsquedas en cadenas


utilizando el condicional LIKE o NOT LIKE y los comodines _
(cualquier carcter) o % (cualquier nmero de caracteres).

Ejemplos de consultas:
Registros en los que el apellido empieza por la cadena recibida:

// EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS


$apellidos = recoge("apellidos");

$consulta = "SELECT COUNT(*) FROM $dbTabla
WHERE apellidos LIKE :apellidos";
$result = $db->prepare($consulta);
$result->execute(array(":apellidos" => "$apellidos%"));
if (!$result) {
print "<p>Error en la consulta.</p>\n";
} else {
print "<p>Se han encontrado " . $result->fetchColumn() .
" registros.</p>\n";
}

Registros en los que el apellido contiene la cadena recibida:

// EJEMPLO DE CONSULTA DE SELECCIN DE REGISTROS


$apellidos = recoge("apellidos");

$consulta = "SELECT COUNT(*) FROM $dbTabla
WHERE apellidos LIKE :apellidos";
$result = $db->prepare($consulta);
$result->execute(array(":apellidos" => "%$apellidos%"));
if (!$result) {
print "<p>Error en la consulta.</p>\n";
} else {
print "<p>Se han encontrado " . $result->fetchColumn() .
" registros.</p>\n";
}

Volver al principio de la pgina

Consultas de unin de tablas

Se pueden tambin realizar consultas de unin entre varias tablas (el


ejemplo est sacado del ejercicio de Biblioteca):

Nota: Escribir como consulta preparada.


// EJEMPLO DE CONSULTA DE UNIN DE TABLAS
$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 {
...

Volver al principio de la pgina

ltima modificacin de esta pgina: 8 de diciembre de 2014

Clases para tabla usuarios y conexion a base de


datos PDO. Seguras?
Estas en el tema de Clases para tabla usuarios y conexion a base de datos PDO. Seguras? en
el foro de Frameworks y PHP orientado a objetos en Foros del Web. Buenas... hace un par de
dias postee mi clase para conectarme y ejecutar sentencias en mysql y me recomendaron
que usara PDO ya que mysql* ...

#1 (permalink)

06/01/2013, 17:39

Fecha de Ingreso: agosto-2009

viclube Mensajes: 46

r Antigedad: 5 aos, 5 meses

Puntos: 1

Clases para tabla usuarios y conexion a base de datos PDO. Seguras?

Buenas... hace un par de dias postee mi clase para conectarme y ejecutar


sentencias en mysql y me recomendaron que usara PDO ya que mysql* queda
obsoleta... ademas de las ventajas que tiene PDO.

As fue que logre conectarme a la base de datos y manejarla desde mi clase


usuarios (en este caso)

Me gustaria que me comentaran si lo estoy haciendo de forma aceptable...


Funcionar... funciona, hasta ahora me sirve y me resulta bastante comodo... pero
es seguro y practico?

Esta clase es solo para obtener los datos para la conexion.

Cdigo PHP:
<?php
class ConfigBD{
public $DBServer;
public $DBName;
public $UserName;
public $Password;
function __construct(){
$this->DBServer='localhost';
$this->UserName='root';
$this->Password='toor';
$this->DBName='db';
}
}
?>

Cdigo PHP:

<?php
include_once 'claseConfigDB.php';

class db{
/*** Declare instance ***/
private static $instance = NULL;

/**
*
* the constructor is set to private so
* so nobody can create a new instance using new
*
*/
private function __construct() {
/*** maybe set the db name here later ***/
}

/**
*
* Return DB instance or create intitial connection
*
* @return object (PDO)
*
* @access public
*
*/
public static function getInstance() {

if (!self::$instance)
{
$conexion = new ConfigBD();
self::$instance = new PDO("mysql:host=$conexion-
>DBServer;dbname=$conexion->DBName",$conexion->UserName,$conexion-
>Password);; //asi funciona y con variables NO.
self::$instance-> setAttribute(PDO::ATTR_ERRMODE, PD
O::ERRMODE_EXCEPTION);
}
return self::$instance;
}

/**
*
* Like the constructor, we make __clone private
* so nobody can clone the instance
*
*/
private function __clone(){
}

} /*** end of class ***/

Y esta es mi clase Usuario para mi tabla de usuarios

Cdigo PHP:
<?php
include_once "claseBaseDatos.php";

class Usuario{

private $id_usuario;
private $nombre;
private $apellido;
private $usuario;
private $pass;
private $rol;
private $email;
private $telefono;
private $activo;

public function __construct(){


$this->idusuario = "";
$this->nombre = "";
$this->apellido = "";
$this->usuario = "";
$this->pass = "";
$this->rol = "";
$this->email = "";
$this->telefono = "";
$this->activo = "";
}
public function cargar($id_usuario = null, $nombre, $apellido, $u
suario, $pass, $rol, $email, $telefono){
$this->setIdUsuario($id_usuario);
$this->setNombre($nombre);
$this->setApellido($apellido);
$this->setUsuario($usuario);
$this->setPass($pass);
$this->setRol($rol);
$this->setEmail($email);
$this->setTelefono($telefono);
}
public function setIdUsuario($id_usuario){
$this->idusuario = $id_usuario;
}
public function setNombre($nombre){
$this->nombre=$nombre;
}
public function setApellido($apellido){
$this->apellido=$apellido;
}
public function setUsuario($usuario){
$this->usuario=$usuario;
}
public function setPass($pass){
$this->pass=$pass;
}
public function setRol($rol){
$this->rol=$rol;
}
public function setEmail($email){
$this->email=$email;
}
public function setTelefono($telefono){
$this->telefono=$telefono;
}
public function setActivo($activo){
$this->activo=$activo;
}

public function getIdUsuario(){


return $this->idusuario;
}
public function getNombre(){
return $this->nombre;
}
public function getApellido(){
return $this->apellido;
}
public function getUsuario(){
return $this->usuario;
}
public function getPass(){
return $this->pass;
}
public function getRol(){
return $this->rol;
}
public function getEmail(){
return $this->email;
}
public function getTelefono(){
return $this->telefono;
}
public function getActivo(){
return $this->activo;
}
public function agregarUsuario(){
$query="INSERT INTO usuarios (id_usuario, nombre, apellido, u
suario, pass, rol, email, telefono) VALUES (:id_usuario, :nombre, :ap
ellido, :usuario, :pass, :rol, :email, :telefono)";
try
{
$comando = DB::getInstance()->prepare($query);
$rows = $comando->execute(array(':id_usuario' => $this-
>getIdUsuario(), ':nombre' => $this->getNombre(), ':apellido' => $thi
s->getApellido(), ':usuario' => $this->getUsuario(), ':pass' => $thi
s->getPass(), ':rol' => $this->getRol(), ':email' => $this-
>getEmail(), ':telefono' => $this->getTelefono()));
if( $rows == 1 ){echo 'INSERT correcto';}
}
catch(PDOException $e)
{
echo 'Error: ' . $e->getMessage();
}
}
public function buscarUsuario($id_usuario){
$query = "SELECT * FROM usuarios WHERE id_usuario=".(int)
$id_usuario;

try {
$comando = DB::getInstance()->prepare($query);
$comando->execute();
if($row = $comando->fetch(PDO::FETCH_ASSOC)) {
$this->setIdUsuario($row['id_usuario']);
$this->setNombre($row['nombre']);
$this->setApellido($row['apellido']);
$this->setUsuario($row['usuario']);
$this->setRol($row['rol']);
$this->setEmail($row['email']);
$this->setTelefono($row['telefono']);
$this->setActivo($row['activo']);

return true;
}else{
echo 'No hay resultado para esa consulta';
return false;
}

}catch(PDOException $e){
echo 'Error: ' . $e->getMessage();
}
}
public static function listarUsuarios(){
$query = "SELECT * FROM usuarios";
try {
$comando = DB::getInstance()->prepare($query);
$comando->execute();
$arreglo = array();
while( $datos = $comando->fetch() ) {

$usuario=new Usuario();
$usuario->cargar($datos[0], $datos[1], $datos[2], $da
tos[3], $datos[4], $datos[5], $datos[6], $datos[7], $datos[8]);
array_push($arreglo,$usuario);
}
return $arreglo;
}catch(PDOException $e){
echo 'Error: ' . $e->getMessage();
}
}

public function eliminarUsuario(){


$query="DELETE FROM usuarios WHERE id_usuario = :id_usuario";

try
{
$comando = DB::getInstance()->prepare($query);
$rows = $comando->execute(array(':id_usuario' => $this-
>getIdUsuario()));
if( $rows == 1 ){echo 'DELETE correcto';}
}
catch(PDOException $e)
{
echo 'Error: ' . $e->getMessage();
}
}

public function editarUsuario(){


try
{
$comando = DB::getInstance()->prepare('UPDATE usuarios SE
T nombre = :nombre, apellido = :apellido, usuario = :usuario, pass =
:pass, rol = :rol, email = :email, telefono = :telefono, activo = :ac
tivo WHERE id_usuario = :id_usuario');
$rows = $comando->execute(array(':nombre' => $this-
>getNombre(), ':apellido' => $this->getApellido(), ':usuario' => $thi
s->getUsuario(), ':pass' => md5($this->getPass()), ':rol' => $this-
>getRol(), ':email' => $this->getEmail(), ':telefono' => $this-
>getTelefono(), ':activo' => $this->getActivo(), ':id_usuario' => $th
is->getIdUsuario()));
if( $rows == 1 ){echo 'UPDATE correcto';}
}
catch(PDOException $e)
{
echo 'Error: ' . $e->getMessage();
}
}
}
?>

Las dos primeras clases las saque de algun lado... diria de donde pero no me
acuerdo, si alguien sabe que le de los creditos jeje.
Recogida de datos

Matriz $_REQUEST

Referencia a $_REQUEST dentro y fuera de cadenas

Comprobacin de existencia

o Controles vacos

o Controles inexistentes: isset()

Seguridad en las entradas

o Eliminar etiquetas: strip_tags($cadena)

o Eliminar espacios en blanco iniciales y


finales: trim($cadena)

Utilizacin de variables

Salida de datos

o El carcter & (ampersand)

o El carcter " (comillas)

o Solucin general: htmlspecialchars()

Funciones de recogida de datos

o Recoger un dato

o Recoger una matriz de una dimensin

o Recoger un dato con valor predeterminado

o Recoger y recortar los datos

Matriz $_REQUEST

Cuando se enva un formulario, PHP almacena la informacin recibida


en una matriz llamada $_REQUEST. El nmero de valores recibidos y
los valores recibidos dependen tanto del formulario como de la accin
del usuario.

Cualquier control se enva solamente si est establecido su


atributo name. El atributo name del control puede contener cualquier
carcter (nmeros, acentos, guiones, etc), pero si contiene espacios,
los espacios se sustituyen por guiones bajos (_). Cada control crea un
elemento de la matriz $_REQUEST, que se identifica como
$_REQUEST[valor_del_atributo_name] y que contiene el valor
entregado por el formulario (en su caso).

El siguiente ejemplo muestra un ejemplo de formulario:

<form action="ejemplo.php">
<p>Nombre: <input type="text" name="nombre" /></p>
<p><input type="submit" value="Enviar" /></p>
</form>

Mientras se est programando, para comprobar que el fichero php


est recibiendo la informacin enviada por el control, lo ms fcil es
utilizar la funcin print_r($matriz) para mostrar el contenido de la
matriz $_REQUEST. Una vez se ha comprobado que la informacin
llega correctamente, la lnea se debe comentar o eliminar.

El siguiente ejemplo muestra lo que escribira el programa PHP si


recibiera la informacin del formulario anterior.

<?php
Nombre: print "<pre>"; print_r($_REQUEST)
Pepito Cone

print "<p>Su nombre es $_REQUEST[


?>
Enviar

Conviene colocar etiquetas <pre> alrededor del print_r($_REQUEST)


para facilitar la visualizacin de los valores.
Volver al principio de la pgina

Referencia a $_REQUEST dentro y fuera de cadenas

Al hacer referencia a los elementos de la matriz $_REQUEST, hay que


tener en cuenta si la referencia se encuentra dentro de una cadena o
fuera de ella.

Si se hace referencia dentro de una caden, el ndice se debe


escribir sin comillas. Si se escribe cualquier tipo de comillas
(simples o dobles) se produce un error.

<?php Su nombre es Pepito Conejo


print "<p>Su nombre es
$_REQUEST[nombre]</p>\n";
?>
<?php Parse error: syntax error, unex
(T_VARIABLE) or number (T_NUM

print "<p>Su nombre es


$_REQUEST["nombre"]</p>\n";
?>
Parse error: syntax error, unex
(T_NUM_STRING) in ejemplo.ph
<?php
print "<p>Su nombre es
$_REQUEST['nombre']</p>\n";
?>

Si se hace referencia fuera de una cadena, el ndice se debe


escribir con comillas dobles o simples. Si se escribe sin comillas,
se produce un aviso.

<?php
print "<p>Su nombre es " . $_REQUEST["nombre"] . "</p>\n";
?>
<?php
print "<p>Su nombre es " . $_REQUEST['nombre'] . "</p>\n";
?>

<?php
print "<p>Su nombre es " . $_REQUEST[nombre] . "</p>\n";
?>
En ningn caso se pueden utilizar los carcteres especiales \" o \', ni
dentro ni fuera de las cadenas.

Parse error: syntax error, unexpect


variable (T_VARIABLE) or number
<?php
print "<p>Su nombre es
$_REQUEST[\"nombre\"]</p>\n";
?>
Parse error: syntax error, unexpect
variable (T_VARIABLE) or number
<?php
print "<p>Su nombre es " .
$_REQUEST[\'nombre\'] . "</p>\n";
?>

Volver al principio de la pgina

Comprobacin de existencia

Un programa PHP no debe suponer que los controles le van a llegar


siempre porque cuando eso no ocurra, el programa no funcionar
correctamente.

Controles vacos

La primera situacin que los programas deben tener en cuenta es que


los controles no contengan ningn valor.

En el programa de ejemplo del principio de esta leccin, si el usuario


ha utilizado el formulario y ha escrito un dato, el programa PHP recibe
el control y funciona correctamente:

ejemplo.html ejemplo.php
<?php
Nombre: print "<pre>"; print_r($_REQUEST);
Pepito Cone "</pre>\n";

print "<p>Su nombre es $_REQUEST[nombre]</p>\


Enviar ?>
Pero si el usuario no escribe nada en el formulario, aunque el
programa funcione correctamente, la respuesta del programa puede
confundir al usuario:

ejemplo.html ejemplo.php
<?php
Nombre: print "<pre>"; print_r($_REQUEST); pri

print "<p>Su nombre es $_REQUEST[nombr


?>
Enviar

Este problema se puede resolver incluyendo una estructura if ...


else ... que considere la posiblidad de que no se haya escrito nada en
el formulario:

ejemplo.html ejemplo.php
<?php
Nombre: print "<pre>"; print_r($_REQUEST); pri

if ($_REQUEST["nombre"] == "") {
print "<p>No ha escrito ningn nom
Enviar
} else {
print "<p>Su nombre es $_REQUEST[n
}
?>

Controles inexistentes: isset()

Un problema ms grave es que el programa suponga que existe un


control que en realidad no le ha llegado.

Eso puede ocurrir, por ejemplo, en el caso de las casillas de


verificacin y los botones radio, ya que los formularios envan el
control solamente cuando se han marcado.

<form action="ejemplo.php">
<p>Deseo recibir informacin: <input type="checkbox" name="acepto" /></p>
<p><input type="submit" value="Enviar" /></p>
</form>
Si en el formulario anterior el usuario marca la casilla, el siguiente
programa funciona perfectamente:

ejemplo.html ejemplo.php
<?php
Deseo recibir informacin: print "<pre>"; print_r($_REQUEST); pri

if ($_REQUEST["acepto"] == "on") {
print "<p>Desea recibir informaci
Enviar
} else {
print "<p>No desea recibir informa
}
?>

Pero si el usuario no marca la casilla, la matriz $_REQUEST no


contiene el dato y el programa genera un aviso por hacer referencia a
un ndice no definido, aunque se haya incluido la estructura if ... else ...
:

ejemplo.html ejemplo.php
<?php
Deseo recibir informacin: print "<pre>"; print_r($_REQUEST); p

if ($_REQUEST["acepto"] == "on") {
print "<p>Desea recibir informac
Enviar
} else {
print "<p>No desea recibir infor
}
?>

Esto puede ocurrir en realidad en cualquier formulario (incluso en


formularios con cajas de texto que en principio envan siempre el
control) ya que el usuario puede escribir directamente la direccin del
programa PHP en el navegador y ejecutar el programa PHP sin pasar
por el formulario. En ese caso, el programa no recibe ningn control y
el programa genera un aviso por hacer referencia a un ndice no
definido:
ejemplo.html ejemplo.php
<?php
Nombre: print "<pre>"; print_r($_REQUEST); p
Pepito Cone

if ($_REQUEST["nombre"] == "") {
print "<p>No ha escrito ningn n
Enviar
} else {
print "<p>Su nombre es $_REQUEST
}
?>

Estos problemas se pueden resolver comprobando que el ndice est


definido antes de hacer referencia a l, utilizando la
funcin isset($variable), que admite como argumento una
variable y devuelve true si existe y false si no existe.

ejemplo.html ejemplo.php
Deseo recibir informacin: <?php
if (isset($_REQUEST["acepto"])) {
print "<p>Desea recibir informacin</p
} else {
Enviar print "<p>No desea recibir informacin
}
?>

ejemplo.html ejemplo.php
Deseo recibir informacin: <?php
if (isset($_REQUEST["acepto"])) {
print "<p>Desea recibir informacin</p
} else {
Enviar print "<p>No desea recibir informacin
}
?>

Es conveniente efectuar siempre la verificacin de existencia para


prevenir los casos en que un usuario intente acceder a la pgina PHP
sin pasar por el formulario.

Volver al principio de la pgina

Seguridad en las entradas


Un usuario puede insertar cdigo html en la entrada de un formulario,
lo que puede acarrear comportamientos inesperados y riesgos de
seguridad. El siguiente ejemplo solamente perjudicara al aspecto de
la pgina:

Nombre: <?php
<strong>Pe
print "<pre>"; print_r($_REQUEST); pri

print "<p>Su nombre es $_REQUEST[nombr


Enviar
?>

El siguiente ejemplo muestra cmo puede utilizarse un formulario para


inyectar en la pgina cdigo JavaScript. En este caso el cdigo genera
una ventana emergente que slo afecta al propio usuario, pero esta
va podra utilizarse para atacar al servidor.

Nota: El ejemplo funciona actualmente (septiembre de 2014) en


Firefox, no funciona en Chrome e Internet Explorer se protege
modificando la pgina.

Nombre: <?php
<body onlo
print "<pre>"; print_r($_REQUEST); print "</

print "<p>Su nombre es $_REQUEST[nombre]</p>


Enviar
?>

Estos dos ejemplos no pondran el riesgo el servidor, pero si en una


aplicacin web los datos que enva el usuario se incluyen sin
precaucin en una consulta SQL estaramos hablando de una va de
ataque al servidor muy peligrosa (puede consultarse una introduccin
elemental a estas tcnicas en la leccin Inyeccin SQL).
Eliminar etiquetas: strip_tags($cadena)

Una manera de resolver el problema anterior es utilizar la


funcin strip_tags($cadena), que devuelve la cadena sin
etiquetas, como muestra el siguiente ejemplo.

Nombre: <?php
<strong>Pe
print "<pre>"; print_r($_REQUEST); print "

print "<p>Su nombre es $_REQUEST[nombre]</


Enviar print "<p>Su nombre es " . strip_ta
"</p>\n";
?>

Aunque hay que tener en cuenta que esta funcin elimina cualquier
cosa que se interprete como una etiqueta, es decir, que empiece por
"<".

Nombre: <?php
<pepe>
print "<pre>"; print_r($_REQUEST); pr

print "<p>Su nombre es " . strip_tags


Enviar ?>

Eliminar espacios en blanco iniciales y finales: trim($cadena)

A veces, al rellenar un formulario, los usuarios escriben por error


espacios en blanco al principio o al final de las cajas de texto y si el
programa PHP no tiene en cuenta esa posibilidad se pueden obtener
resultados inesperados.

En el programa de ejemplo del apartado anterior en el que se escriba


una estructura if ... else ... para avisar al usario si no escriba el
nombre, si el usuario escribe nicamente espacios en blanco, el
programa funciona, pero no da la respuesta adecuada ya que la
cadena con espacios en blanco no es una cadena vaca.

ejemplo.html ejemplo.php
<?php
Nombre: print "<pre>"; print_r($_REQUEST); pri

if ($_REQUEST["nombre"] == "") {
print "<p>No ha escrito ningn nom
Enviar
} else {
print "<p>Su nombre es $_REQUEST[n
}
?>

Este problema se puede resolver utilizando la


funcin trim($cadena), que elimina los espacios en blanco iniciales
y finales y devuelve la cadena sin esos espacios. Modificando el
ejemplo anterior, la cadena introducida queda reducida a la cadena
vaca y la comprobacin la detecta:

ejemplo.html ejemplo.php
Nombre: <?php
print "<pre>"; print_r($_REQUEST); prin
if (trim($_REQUEST["nombre"]) == "") {
print "<p>No ha escrito ningn nomb
Enviar } else {
print "<p>Su nombre es ". trim
"</p>\n";
}
?>

Volver al principio de la pgina

Utilizacin de variables

Hemos visto que para resolver los problemas comentados en los


apartados anteriores, al incluir cualquier referencia a
$_REQUEST[control] habra que :

comprobar que el control est definido con la funcin isset()

eliminar las etiquetas con la funcin strip_tags()

eliminar los espacios en blanco inciales y finales con la


funcin trim()

Una manera de hacerlo sin complicar excesivamente el programa es


guardar los valores de la matriz $_REQUEST en variables y realizar
todas las comprobaciones al definir esas variables. En el resto del
cdigo basta con utilizar la variable en vez del elemento de la matriz
$_REQUEST. En los siguientes ejemplos, se puede ver cmo el
programa no genera avisos y muestra el mensaje correspondiente al
dato recibido:

ejemplo.html ejemplo.php
Nombre: <?php
print "<pre>"; print_r($_REQUEST); print "</pre>\n

if (isset($_REQUEST["nombre"])) {
Enviar $nombre = trim(strip_tags($_REQUEST["nombre"])
} else {
$nombre = "";
}

if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
} else {
print "<p>Su nombre es $nombre</p>\n";
}
?>
ejemplo.html ejemplo.php

Nombre: <?php
<strong>Pe
print "<pre>"; print_r($_REQUEST); print "</pre>\n

if (isset($_REQUEST["nombre"])) {
Enviar $nombre = trim(strip_tags($_REQUEST["nombre"])
} else {
$nombre = "";
}

if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
} else {
print "<p>Su nombre es $nombre</p>\n";
}
?>

La asignacin de la variable se puede realizar en una sola instruccin,


utilizando la notacin abreviada: (condicin) ? verdadero : falso; (que
se explica en la leccin de estructuras de control):

ejemplo.html ejemplo.php
Nombre: <?php
<strong>Pe
print "<pre>"; print_r($_REQUEST); print "</pre>\

$nombre = (isset($_REQUEST["nombre"]))
Enviar ? trim(strip_tags($_REQUEST["nombre"]))
: "";

if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
} else {
print "<p>Su nombre es $nombre</p>\n";
}
?>

Volver al principio de la pgina

Salida de datos

Si, como en los ejemplos anteriores, los datos recogidos se van a


incorporar luego a una pgina web, hay que tener cuidado en algunos
casos especiales. Se comentan a continuacin dos de ellos y despus
se comenta una solucin general para este tipo de caracteres.

El carcter & (ampersand)

Si el usuario escribe en una entrada el carcter & (ampersand, entidad


de carcter &amp;) y esa cadena se escribe en una pgina, la pgina
se ver correctamente en el navegador, pero la pgina no ser vlida
(el xhtml ser invalido).

La pgina es invlida porque el carcter & indica el comienzo de


una entidad de carcter, as que no el cdigo fuente no puede haber
un & sin nada a continuacin. Si se quiere mostrar un carcter & en el
navegador, en el cdigo fuente debe escribirse la entidad de carcter
&amp;.

Nombre: <?php
Pepito & Co
$nombre = (isset($_REQUEST["nombre"]))
? trim(strip_tags($_REQUEST["nombre"]
: "";
Enviar

if ($nombre == "") {
print "<p>No ha escrito ningn nombre
} else {
print "<p>Su nombre es $nombre</p>\n"
}
?>

La solucin es sustituir el carcter & por su entidad de carcter


correspondiente (&amp;). Eso se puede hacer con la
funcin str_replace()

Nombre: <?php
Pepito & Co
$nombre = (isset($_REQUEST["nombre"]))
? trim(strip_tags($_REQUEST["nombre"]
: "";
Enviar
$nombre = str_replace('&', '&amp;', $nomb

if ($nombre == "") {
print "<p>No ha escrito ningn nombre
} else {
print "<p>Su nombre es $nombre</p>\n"
}
?>

El carcter " (comillas)

Si el usuario escribe en una entrada el carcter " (comillas, entidad de


carcter &quot;), si esa cadena se escribe dentro de otras comillas
(por ejemplo, en el atributo value de una etiquetainput), la pgina no
se ver correctamente y adems no ser vlida.

Nombre: <?php
Me llamo
$nombre = (isset($_REQUEST["nombre"]))
? trim(strip_tags($_REQUEST["nombre"]))
: "";
Enviar

if ($nombre == "") {
print "<p>No ha escrito ningn nombre</
} else {
print "<p>Corrija: <input type=\"text\"
}
?>
El problema es que el cdigo fuente contiene comillas dentro de
comillas y el navegador no puede interpretar correctamente el cdigo
(en este caso, el valor del atributo value es solamente "Me llamo":
<p>Corrige: <input type="text" value="Me llamo "Pepe"" /></p>

Como en el caso anterior, la solucin es sustituir el carcter " por su


entidad de carcter correspondiente (&quot;). Eso se puede hacer con
la funcin str_replace()

Nombre: <?php
Me llamo
$nombre = (isset($_REQUEST["nombre"]))
? trim(strip_tags($_REQUEST["nombre"]))
: "";
Enviar
$nombre = str_replace('"', '&quot;', $nombr

if ($nombre == "") {
print "<p>No ha escrito ningn nombre</
} else {
print "<p>Corrija: <input type=\"text\"
}
?>

Solucin general: htmlspecialchars()

Adems de los caracteres ampersand (&) y comillas ("), tambin


podran dar problemas los caracteres comillas simples (') o las
desigualdades (< y >). Se podra sustituir cada uno de ellos como se
ha hecho en los ejemplos anteriores, pero la
funcin htmlspecialchars() realiza todas las sustituciones de una
sola vez.

Nota: PHP 5.4.0 modific el juego de caracteres predeterminado de


esta funcin (de ISO-8859-1 a UTF-8). Si se est trabajando con
ficheros en el cdigo de carcteres ISO-8859-1, se deben aadir dos
argumentos en la llamada a htmlspecialchars(): ENT_QUOTES
e "ISO-8859-1" (sin comillas y con comillas respectivamente).

Nombre: <?php
Me llamo
$nombre = (isset($_REQUEST["nombre"]))
? htmlspecialchars(trim(strip_tags($_REQUEST[
: "";
if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
Enviar
} else {
print "<p>Corrija: <input type=\"text\" value
}
?>

El ejemplo anterior tiene el inconveniente de que la primera funcion


que se aplica es strip_tags() por lo que se elimar todo lo que
est entre marcas (<>).

Nombre: <?php
Me llamo <s
$nombre = (isset($_REQUEST["nombre"]))
? htmlspecialchars(trim(strip_tags($_REQUEST[
: "";
Enviar
if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
} else {
print "<p>Corrija: <input type=\"text\" value
}
?>

Si no queremos quitar etiquetas, pero no queremos que se consideren


como etiquetas sino como texto, habra que aplicar primero la
funcin htmlspecialchars(), como en el ejemplo siguiente.

Nombre: <?php
Me llamo <s
$nombre = (isset($_REQUEST["nombre"]))
? strip_tags(trim(htmlspecialchars($_REQUEST[
: "";
Enviar
if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
} else {
print "<p>Corrija: <input type=\"text\" value
}
?>

En el ejemplo anterior la funcin strip_tags() no hace nada puesto


que las marcas < y > se han cambiado antes por las entidades de
carcter &lt; y &gt; asi que se podra simplificar el programa:

Nombre: <?php
Me llamo <s
$nombre = (isset($_REQUEST["nombre"]))
? trim(htmlspecialchars($_REQUEST["nombre
: "";

if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>
} else {
Enviar
print "<p>Corrija: <input type=\"text\" v
}
?>

Volver al principio de la pgina

Funciones de recogida de datos

La forma ms cmoda de tener en cuenta todos los aspectos


comentados en los puntos anteriores es definir una funcin recoge():

La funcion tendr como argumento el nombre del control que se


quiere recibir y devolver el valor recibido (o una cadena vaca si
el control no se ha recibido).

Para tratar los datos recibidos se aplicarn nicamente las


funciones htmlspecialchars() y trim():
trim(htmlspecialchars($_REQUEST[$var], ENT_QUOTES, "ISO-
8859-1"))

De esta manera, si se reciben etiquetas (<>), las etiquetas no se


borrarn, sino que se conservarn como texto.
Si se quisieran borrar las etiquetas, habra que aplicar las
funciones strip_tags(), trim(), y htmlspecialchars():

htmlspecialchars(trim(strip_tags($_REQUEST[$var])),
ENT_QUOTES, "ISO-8859-1")

Una vez definida la funcin, al comienzo del programa se


almacenarn en variables los datos devueltos por la funcin .

En el resto del programa se trabaja con las variables.

A continuacin se definen varias funciones. Las que ms se utilizarn


en estos apuntes son las dos primeras (recoger un dato y recoger una
matriz):

1. Recoger un dato

2. Recoger una matriz de una dimensin


3. Recoger un dato con valor predeterminado

4. Recoger y recortar los datos

Recoger un dato

La siguiente funcin se podr utilizar en la mayora de ejercicios


propuestos en estos apuntes.
<?php
// FUNCIN DE RECOGIDA DE UN DATO
function recoge($var)
{
$tmp = (isset($_REQUEST[$var]))
? trim(htmlspecialchars($_REQUEST[$var], ENT_QUOTES, "ISO-
8859-1"))
: "";
return $tmp;
}

// EJEMPLO DE USO DE LA FUNCIN ANTERIOR


$nombre = recoge("nombre");
if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>";
} else {
print "<p>Su nombre es $nombre</p>\n";
}
?>

Recoger una matriz de una dimensin

La siguiente funcin se podr utilizar en los ejercicios


propuestos en estos apuntes en los que se recojan matrices.

Si un formulario enva los datos en forma de matriz, como en el


ejemplo siguiente, la funcin recoge() del punto anterior no servira.
<form action="ejemplo.php">
<p>Nombre: <input type="text" name="nombre[1]" /></p>
<p>Apellido: <input type="text" name="nombre[2]" /></p>
<p><input type="submit" value="Enviar" /></p>
</form>

Hay que hacer otra funcin que recoja y trate los datos en forma de
matriz.

La funcion recogeMatriz() del ejemplo siguiente tiene como


argumento el nombre del control que se quiere recibir (que debe
ser una matriz de una dimensin, no sirve para matrices de dos
o ms dimensiones) y devuelve una matriz con los valores
recibidos o una matriz vaca si el control no se ha recibido.
<?php
// FUNCIN DE RECOGIDA DE UNA MATRIZ DE UNA DIMENSIN
function recogeMatriz($var)
{
$tmpMatriz = array();
if (isset($_REQUEST[$var]) && is_array($_REQUEST[$var])) {
foreach ($_REQUEST[$var] as $indice => $valor) {
$indiceLimpio = trim(htmlspecialchars($indice,
ENT_QUOTES, "ISO-8859-1"));
$valorLimpio = trim(htmlspecialchars($valor,
ENT_QUOTES, "ISO-8859-1"));
$tmpMatriz[$indiceLimpio] = $valorLimpio;
}
}
return $tmpMatriz;
}

// EJEMPLO DE USO DE LA FUNCIN ANTERIOR


$nombre = recogeMatriz("nombre");

if ($nombre[1] == "") {
print "<p style=\"color: red\">No ha escrito su
nombre.</p>\n";
} else {
print "<p>Su nombre es <strong>$nombre[1]</strong>.</p>\n";
}

if ($nombre[2] == "") {
print "<p style=\"color: red\">No ha escrito su
apellido.</p>\n";
} else {
print "<p>Su apellido es <strong>$nombre[2]</strong>.</p>\n";
}
?>

Recoger un dato con valor predeterminado

La funcin recoge() anterior se puede modificar para definir un valor


predeterminado (es decir, que si el dato no existe, la funcin devuelve
el valor enviado como segundo argumento y si no se enva el valor
predeterminado la funcin devolvera la cadena vaca).

En el ejemplo siguiente, si no se recibe el nombre, se le asigna el


nombre "pobrecito hablador".
<?php
// FUNCIN DE RECOGIDA DE UN DATO CON VALOR PREDETERMINADO
function recoge($var, $var2="")
{
$tmp = (isset($_REQUEST[$var]) &&
trim(strip_tags($_REQUEST[$var])) != "")
? trim(htmlspecialchars($_REQUEST[$var], ENT_QUOTES, "ISO-
8859-1"))
: trim(htmlspecialchars($var2, ENT_QUOTES, "ISO-8859-1"));
return $tmp;
}

// EJEMPLO DE USO DE LA FUNCIN ANTERIOR


$nombre = recoge("nombre", "pobrecito hablador");
print "<p>Su nombre es $nombre</p>\n";
?>

Recoger y recortar los datos

En caso de que por algn motivo se quiera recortar el tamao de los


datos recibidos a un tamao determinado se puede crear una funcin
recorta() que recorte la longitud de los campos y llamarla desde la
funcin recoge():
<?php
// FUNCIONES DE RECOGIDA Y RECORTE DE UN DATO
$tamNombre = 30;

$recorta = array(
"nombre" => $tamNombre);

function recorta($campo, $cadena)


{
global $recorta;

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

function recoge($var)
{
$tmp = (isset($_REQUEST[$var]))
? trim(htmlspecialchars($_REQUEST[$var], ENT_QUOTES, "ISO-
8859-1"))
: "";
$tmp = recorta($var, $tmp);
return $tmp;
}

// EJEMPLO DE USO DE LAS FUNCIONES ANTERIORES


$nombre = recoge("nombre");
if ($nombre == "") {
print "<p>No ha escrito ningn nombre</p>\n";
} else {
print "<p>Su nombre es $nombre</p>\n";
}
?>

Volver al principio de la pgina

ltima modificacin de esta pgina: 16 de octubre de 2014


Re: comunicacion entre clase y formulario

Respuesta #3 en: 10 Noviembre 2009, 17:20

Cuando envias el formulario de nuevo a esa pgina te falta algo para recoger lo que has
enviado.

Algo asi...

Cdigo:

<?php
$mold_suelo = $_REQUEST['mold_suelo'];
$mold = $_REQUEST['mold'];
?>

Luego en la creacin del objeto le pasas esas dos variables

Cdigo:

$obj = new de_Prueba($mold_suelo, $mold);

Con eso deberia recibir sin problemas los datos.

Un saludo

En lnea

mag55 Re: comunicacion entre clase y formulario

Respuesta #4 en: 10 Noviembre 2009, 19:41


Desconectado

Mensajes: 3
gracias gente ya funciona, hice lo que dijeron, pero ahora hace akgo
extrao, cuando se carga el formulario por primera vez y solo por
primera vez sale esto:

Notice: Undefined index: mold_suelo in


/opt/lampp/htdocs/formularioPruebai.php on line 38

Notice: Undefined index: mold in


/opt/lampp/htdocs/formularioPruebai.php on line 38

en esa linea esta esto

$obj = new de_Prueba($_POST["mold_suelo"], $_POST["mold"]);

luego cuando ingreso los numeros y le doy enviar hace el calculo sin
problemas y estas advertencias desaparecen y puedo seguir ingresando
numeros y haciando el calculo si que esto vuelva a aparecer, si saben
que puede ser se los agradecere
saludos y gracias

12.1. Creando un formulario sencillo


Imagina que ests construyendo una aplicacin lista de tareas. Como tus
usuarios tendrn que editar y crear tareas, vas a necesitar un formulario. Pero
antes de empezar, vamos a centrarnos en la clase genrica Task que
representa y almacena los datos para una sola tarea:

// src/Acme/TaskBundle/Entity/Task.php

namespace Acme\TaskBundle\Entity;

class Task

// descripcin de la tarea

protected $task;

// fecha en la que debe estar completada

protected $dueDate;

public function getTask()

return $this->task;

public function setTask($task)

$this->task = $task;

}
public function getDueDate()

return $this->dueDate;

public function setDueDate(\DateTime $dueDate = null)

$this->dueDate = $dueDate;

NOTA Si ests programando este ejemplo a medida que lees el captulo, primero
crea el bundleAcmeTaskBundle ejecutando el siguiente comando (y acepta los
valores por defecto de todas las opciones):

$ php app/console generate:bundle --namespace=Acme/TaskBundle

Esta clase no es ms que una clase PHP normal, ya que por el momento no
tiene nada que ver ni con Symfony ni con ninguna otra librera. Se trata de una
clase PHP adecuada para resolver el problema de tu aplicacin. Al finalizar
este captulo, sers capaz de enviar informacin a una instancia de la
clase Task mediante un formulario, validar sus datos y guardarlos en una base
de datos.

12.1.1. Construyendo el formulario

Ahora que has creado una clase Task, el siguiente paso consiste en crear y
mostrar en el navegador el formulario HTML real. En Symfony2, esto se hace
construyendo un objeto de tipo Form y luego renderizndolo en una plantilla.
Por el momento vamos a hacer todos estos pasos dentro de un controlador:

// src/Acme/TaskBundle/Controller/DefaultController.php

namespace Acme\TaskBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller

public function newAction(Request $request)

// crea una task y le asigna algunos datos ficticios para este ejemplo

$task = new Task();

$task->setTask('Write a blog post');

$task->setDueDate(new \DateTime('tomorrow'));

$form = $this->createFormBuilder($task)

->add('task', 'text')

->add('dueDate', 'date')

->add('save', 'submit')

->getForm();

return $this->render('AcmeTaskBundle:Default:new.html.twig', array(

'form' => $form->createView(),

));

TRUCO Este ejemplo muestra cmo crear el formulario directamente en el


controlador. Ms adelante en este mismo captulo aprenders a construir tu
formulario en una clase independiente, lo que es muy recomendable para hacer
que tu formulario sea reutilizable.

La creacin de un formulario requiere de muy poco cdigo, porque los


objetos Form de Symfony2 se crean con un "generador de formularios" (o form
builder en ingls). El generador de formularios te permite definir los formularios
con unas instrucciones sencillas y despus l se encarga de todo el trabajo
duro de crear realmente el formulario.

En este ejemplo, se han aadido dos campos al formulario ( task y dueDate)


que se corresponden con las propiedades task y dueDate de la clase Task.
Tambin se asigna un tipo de dato a cada campo ( text y date), para que
Symfony sepa qu tipo de etiqueta HTML debe mostrar para cada campo. Por
ltimo, se ha aadido un botn para enviar el formulario al servidor.

La opcin de aadir botones a los formularios est disponible desde la versin


2.3 de Symfony. En las versiones anteriores tienes que aadir los botones a
mano mediante el cdigo HTML correspondiente.

Symfony2 incluye muchos tipos de datos, tal y como se explicar en las


prximas secciones.

12.1.2. Mostrando el formulario

Una vez creado el formulario, el siguiente paso es renderizarlo. Para ello


puedes pasar a la plantilla un objeto especial de formulario (que se crea con el
mtodo $form->createView()) y tambin puedes utilizar los
diferentes helpers definidos por Symfony:

TWIG

PHP

<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php -->

<?php echo $view['form']->form($form) ?>

Figura 12.1 Resultado de mostrar un formulario sencillo


NOTA El cdigo del ejemplo anterior supone que el formulario se enva mediante
una peticin POSTa la misma URL que se accedi para mostrar el formulario.
Ms adelante se explicar cmo modificar el mtodo utilizado y la URL a la que
se envan los datos.

Y ya est! Al renderizar el helper form(form), se renderizan todos los campos


del formulario, cada uno con su etiqueta <label> y su mensaje de error (si es
que se ha producido algn error). La funcin form tambin encierra todos los
contenidos en la correspondiente etiqueta <form> de HTML. Aunque es una
forma muy sencilla de mostrar un formulario completo, no es muy flexible.
Normalmente es preferible renderizar cada campo individualmente para poder
controlar con precisin cmo se muestra. Esto se explica ms adelante en este
mismo captulo.

Antes de continuar, observa cmo el campo task renderizado tiene el valor de


la propiedad task del objeto $task (en este ejemplo, vers el texto Write a blog
post). Esta es la primera tarea importante del formulario: obtener los datos de
un objeto y transformarlos para que se puedan mostrar correctamente en un
formulario.

TRUCO El sistema de formularios es lo suficientemente inteligente como para


acceder al valor de la propiedad protegida task a travs de los
mtodos getTask() y setTask() de la clase Task. A menos que una propiedad sea
pblica, es obligatorio aadir los getters y setters para que el
componenteForm pueda obtener y guardar datos en la propiedad. Para las
propiedades booleanas, puedes utilizar un mtodo isser (por
ejemplo, isPublished()) o hasser (por ejemplo, hasReminder()) en lugar de
un getter (por ejemplo, getPublished() o getReminder()).

12.1.3. Procesando el envo del formulario

La segunda responsabilidad del formulario consiste en traducir los datos


enviados por el usuario a las propiedades del objeto. Para ello, primero se
guardan en el formulario los datos enviados por el usuario, tal y como muestra
el siguiente ejemplo:

// ...

public function newAction(Request $request)


{

// crear un objeto $task nuevo (borra los datos de prueba)

$task = new Task();

$form = $this->createFormBuilder($task)

->add('task', 'text')

->add('dueDate', 'date')

->add('save', 'submit')

->getForm();

$form->handleRequest($request);

if ($form->isValid()) {

// guardar la tarea en la base de datos

return $this->redirect($this->generateUrl('task_success'));

// ...

El mtodo handleRequest() est disponible desde la versin 2.3 de Symfony. En


las versiones anteriores se pasaba el objeto $request al mtodo submit(). Este
ltimo comportamiento se ha declarado obsoleto y se eliminar en Symfony
3.0.

El controlador del ejemplo anterior sigue el flujo de trabajo habitual para el


manejo de formularios, que consiste en:

1. Cuando se carga por primera vez la pgina asociada a este controlador, se


crea y renderiza el formulario. El mtodo handleRequest() detecta que el
formulario no se ha enviado y por tanto, no hace nada. El
mtodo isValid() devuelve false si el formulario no se ha enviado.
2. Cuando el usuario enva el formulario, el mtodo handleRequest() lo detecta y
guarda inmediatamente los datos enviados en las
propiedades task y dueDate del objeto $task. Despus se valida este objeto. Si
no es vlido, el mtodo isValid() devuelve false otra vez, por lo que se vuelve a
mostrar el formulario, esta vez con los mensajes de error correspondientes. Si
solamente quieres comprobar si el formulario se ha enviado, independientemente
de si es vlido o no, utiliza el mtodo isSubmitted().

3. Cuando el usuario enva el formulario con datos vlidos, los datos enviados se
guardan de nuevo en el formulario, pero esta vez el
mtodo isValid() devuelve true. En este momento ya puedes trabajar con el
objeto $task (por ejemplo guardndolo en una base de datos) antes de redirigir al
usuario a otra pgina (por ejemplo a la pgina de agradecimiento o a la que
muestra un mensaje determinado).

NOTARedirigir a un usuario despus de enviar el formulario evita que el usuario


pueda pulsar sobre el botn Actualizar del navegador provocando un nuevo
envo del formulario.

12.1.4. Enviando formularios con varios botones

Esta caracterstica solamente est disponible desde la versin 2.3 de Symfony.

Cuando el formulario contiene ms de un botn de envo, es necesario


comprobar en el controlador qu botn concreto se ha pulsado. En primer lugar,
modifica ligeramente el formulario de los ejemplos anteriores para aadir un
segundo botn llamado "Save and add":

$form = $this->createFormBuilder($task)

->add('task', 'text')

->add('dueDate', 'date')

->add('save', 'submit')

->add('saveAndAdd', 'submit')

->getForm();

Dentro del controlador, utiliza el mtodo isClicked() para comprobar si se ha


pulsado el botn "Save and add":
if ($form->isValid()) {

// ... haz algo con los datos, como por ejemplo guardarlos

// en la base de datos ...

$nextAction = $form->get('saveAndAdd')->isClicked()

? 'task_new'

: 'task_success';

return $this->redirect($this->generateUrl($nextAction));

Das könnte Ihnen auch gefallen