1.4. Capa de abstraccion PEAR::DB

1.4. Capa de abstraccion PEAR::DB Dataprix 5 Noviembre, 2009 - 11:32
Nota
Hay otras funciones que tienen el mismo cometido que combinaciones de algunas de las funciones comentadas anteriormente (por ejemplo, pg_select o pg_insert, pg_copy_from), pero que no se comentan en este material por su extensión y por su poco uso.

El PEAR (PHP extension and application repository) se define como un marco de trabajo y un sistema de distribución de librerías reutilizables para PHP. Es similar en concepto al CPAN (comprehensive perl archive network) del lenguaje Perl o al PyPI (Python package index) de Python.

El PEAR pretende proporcionar una librería estructurada de código y librerías reutilizables, mantener un sistema para proporcionar a la comunidad herramientas para compartir sus desarrollos y fomentar un estilo de codificación estándar en PHP.

PEAR, debe ser el primer recurso para solventar cualquier carencia detectada en las funciones nativas de PHP. Como buena práctica general en el mundo del software libre, siempre es mejor usar, aprender o mejorar a partir de lo que han hecho otros, que proponernos reinventar la rueda. Además, si hacemos mejoras a las librerías que usemos de PEAR, siempre podemos contribuir a esos cambios mediante las herramientas que nos proporciona.

En cierta forma, PEAR se comporta como un gestor de paquetes más de los que pueden  incorporar  las  distribuciones  GNU/Linux  más  recientes  (como  apt, yum o YOU). Este

PEAR

Si nuestra instalación de PHP es reciente, ya dispondremos de PEAR instalado (a no ser que lo hayamos compilado con la opción
--without-pear.

gestor de paquetes se compone del ejecutable ‘pear’ al que podemos proporcionar un conjunto de parámetros según las acciones que deseemos realizar:

 

 

 

 

$ pear list
Installed packages:
===================
Package        Version      State
Archive_Tar    1.2          stable
Console_Getopt 1.2          stable
DB             1.6.8        stable
http           1.3.3        stable
Mail           1.1.4        stable
Net_SMTP       1.2.6        stable
Net_Socket     1.0.5        stable
PEAR           1.3.4        stable
PhpDocumentor  1.3.0RC3     beta
XML_Beautifier 1.1          stable
XML_Parser     1.2.2        stable
XML_RPC        1.1.0        stable
XML_Util       1.1.1        stable

PEAR (y PHP) ya viene con un conjunto de paquetes instalados, lo que se denomina el PFC (PHP foundation classes). Estos paquetes proporcionan a PHP la mínima funcionalidad necesaria para que PEAR funcione y para que dispongamos de las librerías básicas de PHP.

A continuación presentamos las opciones más habituales de PEAR:
 

Comando Resultado
pear list Lista de los paquetes instalados.
pear list-all Lista de todos los paquetes disponibles en PEAR.
pear list-upgrades Lista de los paquetes instalados con actualización disponible.
pear info <paquete> Proporciona información sobre el paquete.
pear install <paquete> Descarga e instala el paquete.
pear search <texto> Busca paquetes en el repositorio PEAR.
pear upgrade <paquete> Actualiza el paquete si es necesario.
pear upgrade-all Actualiza todos los paquetes instalados con actualización disponible.
pear uninstall <paquete> Desinstala el paquete.

 

 

 

 

 

 

 

 

 

 

1.4.1. Capa de abstraccion del motor de la base de datos

1.4.1. Capa de abstraccion del motor de la base de datos Dataprix 5 Noviembre, 2009 - 11:39

Parece evidente que, para la inmensa mayoría de aplicaciones basadas en PHP, el uso de su librería nativa de acceso a bases de datos va a condicionar el SGBD a usar con la aplicación. En aplicaciones comerciales, o que no pueden ni desean estar cerradas a un único motor, no será imprescindible disponer de unas funciones que encapsulen la comunicación con el SGBD y que sean independientes de éste en las interfaces que ofrecen, mientras que internamente llamarán a las funciones nativas del SGBD concreto con que se esté trabajando en cada momento.

Versiones
La versión 1.6.8. era la más actualizada en el momento de elaboración de este material
(finales de 2004).

Así pues, y buscando en PEAR, encontramos el módulo ‘DB’, una capa de abstracción y encapsulamiento de la comunicación con el SGBD. Al tener que incorporar todas las funcionalidades de los motores que soporta, el resultado será siempre el mínimo conjunto de prestaciones comunes a todos los SGBD. Las prestaciones más destacadas que ofrece la versión actual 1.6.8 son las siguientes:

•    Interfaz orientada a objetos.

•    Una sintaxis común para identificar SGBD y cadenas de conexión.

•    Emulación de “sentencias preparadas” en los motores que no las soportan.

•    Códigos de errores comunes.   

•    Emulación de secuencias o autoincrementos en SGBD que no los soportan.

•    Soporte para transacciones.

•    Interfaz para obtener información del metadato (información sobre la tabla o la base de datos).

•    Compatible con PHP4 y PHP5.

•    Motores soportados: dbase, fbsql, interbase, informix, msql, mssql, mysql, mysqli, oci8, odbc, pgsql, sqlite y sybase.

1 &lt;?php
2 // Incluimos la librería una vez instalada mediante PEAR
3 require_once `DB.php´;
4
5 // Creamos la conexión a la base de datos, en este caso PostgreSQL
6 $db =&amp; DB::connect(`pgsql://usuario:password@servidor/basededatos´);
7
8 // Comprobamos error en la conexión
9 if (DB::isError($db)) {
10  die($db-&gt;getMessage());
11 }
12
13 // Realizamos la consulta:
14 $res =&amp; $db-&gt;query(`SELECT * FROM clients´);
15
16 // Comprobamos que la consulta se ha realizado correctamente
17 if (DB::isError($res)) {
18 die($res-&gt;getMessage());
19 }
20
21 // Iteramos sobre los resultados
22 while ($row =&amp; $res-&gt;fetchRow()) {
23    echo $row[0] . "\n";
24 }
25
26 // Liberamos la hoja de resultados
27 $res-&gt;free()
28
29 // Desconectamos de la base de datos
30 $db-&gt;disconnect();
31 ?&gt;

La estructura del código y hasta la sintaxis de las sentencias es similar a los ejemplos nativos vistos anteriormente, exceptuando las partes de las sentencias que hacían referencia al motor de base de datos en particular.

A continuación, vamos a avanzar por el código ampliando la información sobre cada paso. La conexión se especifica mediante una sintaxis de tipo DSN (data source name). Los DSN admiten multitud de variantes, dependiendo del motor al que nos conectemos, pero en casi todos los casos, tienen la forma siguiente:

motorphp://usuario:contraseña@servidor/basededatos?opcion=valor

 

•  Conexión a MySQL 

mysql://usuario:password@servidor/basededatos

•  Conexión a MySQL a través de un socket UNIX:

mysql://usuario:password@unix(/camino/al/socket)/basededatos

•  Conexión a PostgreSQL

pgsql://usuario:password@servidor/basededatos

•  Conexión a PostgreSQL en un puerto específico:

pgsql://usuario:password@tcp(servidor:1234)/basededatos

 

En cualquier llamada a un método del paquete DB, éste puede devolver el objeto que le corresponde (una hoja de resultados, un objeto representando la conexión, etc.) o bien un objeto que represente el error que ha tenido la llamada. De ésta manera, para comprobar los errores que puede originar cada sentencia o intento de conexión, bastará con comprobar el tipo del objeto devuelto:

8 // Comprobamos error en la conexión
9 if (DB::isError($db)) {
10  die($db-&gt;getMessage());
11 }

La clase DB_Error ofrece varios métodos. A pesar de que el más utilizado es getMessage(), getDebugInfo() o getCode() pueden ampliar la información sobre el error.

Para realizar consultas, disponemos de dos mecanismos diferentes:

•    Enviar la consulta directamente al SGBD.

•    Indicar al gestor que prepare la ejecución de una sentencia SQL y posteriormente indicarle que la ejecute una o más veces.

En la mayoría de los casos, optaremos por el primer mecanismo y podremos proceder como sigue:

13 // Realizamos la consulta:
14 $res =&amp; $db-&gt;query(`SELECT * FROM clients´);
Ejemplo
Pensemos en un conjunto de actualizaciones o inserciones seguidas o en un conjunto de consultas donde vamos cam- biando un intervalo de fechas o el identificador del cliente sobre el que se realizan.

En ocasiones nos podemos encontrar ejecutando la misma consulta varias veces, con cambios en los datos o en el valor de las condiciones. En estos casos, es más indicado utilizar el segundo mecanismo.

Supongamos que tenemos que insertar un conjunto de clientes en nuestra base de datos. Las sentencias que ejecutaríamos serían parecidas a las siguientes:

 

 

INSERT INTO Clientes (nombre, nif) VALUES (‘José Antonio Ramírez’,’29078922Z’);
INSERT INTO Clientes (nombre, nif) VALUES (‘Miriam Rodríguez’,’45725248T’);
...

En lugar de esto, podemos indicarle al motor de la base de datos que prepare la sentencia, del modo siguiente:

$sth = $db-&gt;prepare(`INSERT INTO Clientes (nombre,nif) VALUES (?,?)´);

Utilizaremos la variable $sth que nos ha devuelto la sentencia prepare cada vez que ejecutemos el query:

$db-&gt;execute($sth, `José Antonio Ramírez´,`29078922Z´);
$db-&gt;execute($sth, `Miriam Rodríguez´,`45725248T´);

 

También podemos pasar una array con todos los valores:

 

$datos = array(`Miriam Rodríguez´,`45725248T´);
$db-&gt;execute($sth, $datos);

Y tenemos la opción de pasar una array de dos dimensiones con todas las sentencias a ejecutar, mediante el método executeMultiple():

$todosDatos = array(array(`José Antonio Ramírez´,`29078922Z´),
              array(`Miriam Rodriguez´,`45725248T´));
$sth = $db-&gt;prepare(`INSERT INTO Clientes (nombre,nif) VALUES (?, ?)´);
$db-&gt;executeMultiple($sth, $todosDatos);

A continuación, examinaremos los métodos de iterar sobre los resultados. En el ejemplo inicial hemos utilizado la función fetchRow() de la manera siguiente:

21 // Iteramos sobre los resultados
22 while ($row =&amp; $res-&gt;fetchRow()) {
23    echo $row[0] . "\n";
24 }

Pero también disponemos de la función fetchInto(), que recibe como parámetro el array donde queremos que se almacene el resultado:

21 // Iteramos sobre los resultados
22 while ($res-&gt;fetchRow()) {
23   echo $row[0] . "\n";
24 }

Tanto fetchRow() como fetchInto() aceptan otro parámetro para indicar el tipo de estructura de datos que va a almacenar en $row:

•    DB_FETCHMODE_ORDERED: es la opción por defecto. Almacena el resultado en una array con índice numérico.

•    DB_FETCHMODE_ASSOC: almacena el resultado en una array asociativa en el que las claves son el nombre del campo.

•    DB_FETCHMODE_OBJECT: almacena el resultado en un objeto donde dispondremos de atributos con el nombre de cada campo para obtener el valor en cada iteración.

21 // Iteramos sobre los resultados en modo asociativo
22 while ($res-&gt;fetchInto (($row,DB_FETCHMODE_ASSOC)) {
23    echo $row[`nombre´] . "\n";
24    echo $row[`nif´] . "\n";
25 }

o bien:

21 // Iteramos sobre los resultados en modo objeto
22 while ($res-&gt;fetchInto (($row,DB_FETCHMODE_OBJECT)) {
23    echo $row-&gt;nombre . "\n";
24    echo $row-&gt;nif . "\n";
25 }

La función fetchInto() acepta un tercer parámetro para indicar el número de fila que queremos obtener, en caso de que no deseemos iterar sobre la propia función:

21 // Obtenemos la tercera fila de la consulta
22 $res-&gt;fetchInto ($row,DB_FETCHMODE_ASSOC,3); {
23    echo $row-&gt;nombre . "\n";
24    echo $row-&gt;nif . &amp;"\n";

Hay otros métodos para obtener diferentes vistas del resultado como los siguientes:

•    getAll(): obtiene una array de dos dimensiones con toda la hoja de resultados. Tendría una forma como la siguiente:

Array
(
  [0] =&gt; Array
    (
     [cf] =&gt; Juan
     [nf] =&gt; 5
     [df] =&gt; 1991-01-11 21:31:41
    )
  [1] =&gt; Array
(
  [cf] =&gt; Kyu
  [nf] =&gt; 10
  [df] =&gt; 1992-02-12 22:32:42
)
)

 

•    getRow(): devuelve sólo la primera fila de la hoja de resultados.

•    getCol(): devuelve sólo la columna indicada de la hoja de resultados.

De la misma manera que en las librerías nativas, hay métodos que proporcionan información sobre la consulta en sí:

•    numRows(): número de filas de la hoja de resultados.

•    numCols(): número de columnas de la hoja de resultados.

•    affectedRows(): número de filas de la tabla afectadas por la sentencia de actualización, inserción o borrado.

1.4.2. Transacciones

1.4.2. Transacciones Dataprix 6 Noviembre, 2009 - 11:17

PEAR::DB proporciona mecanismos para tratar las transacciones independientemente del SGBD con que trabajemos.

Como ya hemos comentado, la operativa con las transacciones está relacionada con las sentencias begin, commit y rollback de SQL. PEAR::DB envuelve estas sentencias en llamadas a métodos suyos, del modo siguiente:

           

 

Atención
En MySQL sólo funcionará el soporte de transacciones si la base de datos está almacenada con el mecanismo InnoDB. En PostgreSQL no hay restricción alguna.
En ningún sitio...
... se hace un begin. Al desactivar el autocommit (que está activado por defecto) todas las sentencias pasarán a formar parte de una transacción, que se registrará como definitiva en la base de datos al llamar al método commit() o bien se desechará al llamar al método rollback(), volviendo la base de datos al estado en el que estaba después del último commit().

 

// Desactivamos el comportamiento de COMMIT automático.
$db-&gt;autocommit(false);
..
..
if (...) {
   $db-&gt;commit();
} else {
$db-&gt;rollback();
}

 

 

1.4.3. Secuencias

1.4.3. Secuencias Dataprix 6 Noviembre, 2009 - 11:40

PEAR::DB incorpora un mecanismo propio de secuencias (AUTO_INCREMENT en MySQL), que es independiente de la base de datos utilizada y que puede ser de gran utilidad en identificadores, claves primarias, etc. El único requisito es que se usen sus métodos de trabajo con secuencias, siempre que se esté trabajando con esa base de datos; es decir, no se debe crear la secuencia en el SGBD y, después, trabajar con ella con los métodos que ofrece PEAR::DB. Si la secuencia la creamos mediante la base de datos, entonces deberemos trabajar con ella con las funciones extendidas de SQL que nos proporcione ese SGBD (en PostgreSQL la función nextval() o en MySQL la inserción del valor 0 en un campo AUTO_INCREMENT).

Disponemos de las siguientes funciones para trabajar con secuencias:

•    createSequence($nombre_de_secuencia): crea la secuencia o devuelve un objeto DB_Error en caso contrario.

•    nextId($nombre_de_secuencia):  devuelve  el  siguiente  identificador de la secuencia.

•    dropSequence($nombre_de_secuencia): borra la secuencia.

// Creamos la secuencia:
$tmp = $db-&gt;createSequence(`miSecuencia´);
if (DB::isError($tmp)) {
die($tmp-&gt;getMessage());
}
// Obtenemos el siguiente identificador
$id = $db-&gt;nextId(`mySequence´);
if (DB::isError($id)) {
die($id-&gt;getMessage());
}
// Usamos el identificador en una sentencia
$res =&amp; $db-&gt;query("INSERT INTO miTabla (id, texto) VALUES ($id, `Hola´)");
// Borramos la secuencia
$tmp = $db-&gt;dropSequence(`mySequence´);
if (DB::isError($tmp)) {
die($tmp-&gt;getMessage());
}

 

Finalmente,  en  el  aspecto  relacionado  con  los  metadatos  de  las  tablas, PEAR::DB ofrece la función tableInfo(), que proporciona información detallada sobre una tabla o sobre las columnas de una hoja de resultados obtenida de una consulta.

$info = $db-&gt;tableInfo(`nombretabla´);
print_r($info);

O bien:

$res =&amp; $db-&gt;query(`SELECT * FROM nombretabla´);
$info = $db-&gt;tableInfo($res);
print_r($info);

El resultado será similar al siguiente:

[0] =&gt; Array (
  [table] =&gt; nombretabla
  [name] =&gt; nombre
       [type] =&gt; string
       [len] =&gt; 255
       [flags] =&gt;
     )
     [1] =&gt; Array (
       [table] =&gt; nombretabla
       [name] =&gt; nif
       [type] =&gt; string
       [len] =&gt; 20
       [flags] =&gt; primary key not null
    )
Funcionalidades
Hay funcionalidades más avan- zadas de esta librería que aumentan continuamente. De todas formas, con las presentadas basta para identificar las ventajas de trabajar con una capa de abstracción del motor de base de datos donde se al- macenan los datos de nuestra aplicación.