« Volver al listado completo

Programación web con PHP

Módulo 2.6: Librerías orientadas a objetos

Espacio de nombres

Los espacios de nombres son una manera de agrupar definiciones de clases, funciones y constantes, de manera que no afecten a otras agrupaciones de definiciones similares.

Esto nos permite poner el mismo nombre a estas definiciones de clases, funciones o constantes que otras internas de PHP, o del código de otros desarrolladores que estemos utilizando en nuestro proyecto.

Los espacios de nombres se declaran utilizando la palabra reservada namespace, al principio del fichero antes de cualquier código o de que se haya enviado contenido HTML al navegador.

namespace MiProyecto;
class MiClase { /* ... */ }

También es posible declarar subespacios de nombre, utilizando el carater \ para definir los subespacios tal como se realizaría en una jerarquía de ficheros.

namespace MiProyecto\SubNivel;
const CONECTAR_OK = 1;
class Conexion { /* ... */ }
function conectar() { /* ... */  }

Cualquier definición que se realice fuera de cualquier espacio de nombres se considera dentro del espacio de nombres raíz representado tan solo por \.

Al invocar código de un espacio de nombres desde otro, podemos tener en cuenta si tienen algo en común en su jerarquía para solo especificar los subespacios de nombres necesarios, o especificar las definiciones desde el espacio de nombres raíz.

  Fichero ejemplo: inicio.php

<?php
/* fichero: inicio.php */
function funcionRaiz() {
    echo 'funcionRaiz<br>';

    // llamada a funcion dentro del mismo espacio raíz
    otraFuncionEjemplo();

    // llamadas equivalentes a otro espacio de nombres
    EjemploCurso\funcionEjemplo();
    \EjemploCurso\funcionEjemplo();

    // llamadas equivalentes a un subespacio de nombres
    EjemploCurso\Subespacio\functionSubespacio();
    \EjemploCurso\Subespacio\functionSubespacio();
}
function otraFuncionEjemplo() {
	echo 'otraFuncionEjemplo<br>';
}
include_once 'espacio.php';
include_once 'subespacio.php';

funcionRaiz();

Fichero ejemplo: espacio.php

<?php
/* fichero: espacio.php */
namespace EjemploCurso;

include_once 'subespacio.php';

function funcionEjemplo() {
    echo 'EjemploCurso : funcionEjemplo<br>';

    // llamadas equivalentes en el mismo espacio
    otraFuncionEjemplo();
    \EjemploCurso\otraFuncionEjemplo();

    // llamadas equivalentes a un subespacio
    Subespacio\otraFuncionEjemplo();
    \EjemploCurso\Subespacio\otraFuncionEjemplo();

    // llamada a espacio de nombres raíz
    \otraFuncionEjemplo();
}
function otraFuncionEjemplo() {
	echo 'EjemploCurso : otraFuncionEjemplo<br>';
}

Fichero ejemplo: subespacio.php

<?php
/* fichero: subespacio.php */
namespace EjemploCurso\Subespacio;

function functionSubespacio() {
    echo 'EjemploCurso\Subespacio : funcionSubespacio<br>';

    // llamadas equivalentes dentro del mismo subespacio
    \EjemploCurso\Subespacio\otraFuncionEjemplo();
    otraFuncionEjemplo();

    // llamada a espacio de nombres raíz
    \otraFuncionEjemplo();
}
function otraFuncionEjemplo() {
    echo 'EjemploCurso\Subespacio : otraFuncionEjemplo<br>';
}

Convención de uso de nombres

Como habrás podido comprobar, en PHP el convenio de uso de nombres de funciones no es nada homogeneo, y ha ido cambiando en el tiempo:

Tenemos por ejemplo:

La recomendación actual, es utilizar camelCase (la primera letra en minúsculas, luego cada palabra en mayúsculas sin separador) para funciones, variables, atributos y métodos; y PascalCase (igual pero la primera letra en mayúsculas) para clases y espacios de nombres.

Si quieres averiguar más información sobre convenciones de código, te interesa revisar la web del FIG (Framework Interoperativility Group), concretamente su estandard PSR-4

http://www.php-fig.org/ http://www.php-fig.org/psr/psr-4/

En resumen, los espacios de nombres según esta especificación deben ser de de esta forma:

\<Vendor>\<0-n subnamespaces>\<Class name>

Con algunos puntos adicionales:

La ventaja de seguir estas recomendaciones son dos:

Librería PEAR

PEAR es un framework y sistema de distribución de componentes reutilizables en PHP. Está gestionado y promovido directamente por el mismo equipo de PHP.

http://pear.php.net

Aunque existe una multitud de frameworks en PHP de todo tipo a nuestra disposición, PEAR se diferencia en ser uno de los primeros y con más tiempo de existencia. No intenta convertirse en una solución integral para todo nuestro proyecto, sino en proveer de elementos adicionales a las instrucciones del lenguaje PHP, escritas así mismo en PHP también.

En ocasiones es habitual encontrar PEAR instalado a veces en los hospedajes, y su sistemática de uso está pensada para no necesitar toda la librería de componentes para trabajar con ella, sino solo aquellas clases para la funcionalidad concreta que nos interese.

En nuestro caso al utilizar XAMPP encontraremos PEAR ya instalado en una carpeta como xampp\php\pear, y tan solo utilizando include nombre_archivo_pear.php; sin especificar directorio podremos incluir automáticamente cualquier clase PEAR.

Envío de emails

Para enviar un email en PHP podemos utilizar la función mail() de una manera muy sencilla.

http://php.net/manual/es/function.mail.php

La función mail() opera de manera diferente en Windows que en Linux, por lo que para probar código desarrollado en Windows que luego será desplegado en un hospedaje Linux, es útil tener instalado en el servidor web un emilador de Sendmail como hace XAMPP.

http://stackoverflow.com/questions/15965376/how-to-configure-xampp-to-send-mail-from-localhost

http://www.cristalab.com/tutoriales/envio-de-correos-desde-un-servidor-local-c90973l/

Para opciones de envío de emails avanzadas, como enviar ficheros adjuntos, podemos añadir las cabeceras necesarias según los estándares directamente a mail(), pero es más sencillo utilizar otras librerías que permiten hacerlo de manera más sencilla, como los paquetes PEAR Mail y Mail_Queu. Esto además nos permite conectar con un servidor SMTP para el envío en lugar de utilizar sendmail.

http://pear.php.net/package/Mail

http://pear.php.net/package/Mail_Mime

http://pear.php.net/package/Mail_Queue

Manipulación de XML

La manipulación de ficheros XML desde PHP es bastante sencilla gracias a la existencia de una serie de funciones de librería especializadas en XML.

Desde librerías para manipulación del DOM, como otras genéricas como libxml, SimpleXML, XMLReader y XMLWriter, todas son buenas alternativas para realizar el análisis y la modificación de documentos XML sin necesidad de que tengamos que escribir a mano instrucciones que busquen en cadenas de texto cada elemento XML con el que queramos trabajar.

http://php.net/manual/es/refs.xml.php

A continuación un ejemplo donde al utilizar SimpleXML, quedan cargada la jerarquía de un documento XML como objetos hijos de aquellas etiquetas que lo contienen.

$movies = new SimpleXMLElement($xmlstr);

/* De cada nodo <character> (personaje), mostramos el actor que lo interpreta con <name>. */

foreach ($movies->movie->characters->character as $character) {
   echo $character->name.' interpretado por '.$character->actor.'<br>';
}

Más info en: http://php.net/manual/en/simplexml.examples-basic.php

PHP y bases de datos, PDO, MySQL

PHP cuenta con una serie de extensiones diferentes para conectar con distintos motores de base de datos. Concretamente con MySQL cuenta con al menos las siguientes:

Esta última PDO (PHP Database Objects) es la que nos interesa utilizar, ya que se trata de la más moderna, tiene un mejor rendimiento, es más segura frente a ataques de inyección de código SQL, y además es compatible con múltiples motores de base de datos, no solo con MySQL.

La forma de utilizar las extensiones antiguas de MySQL era un poco similar a los ejemplos que hemos utilizado sobre manejo de ficheros. PDO sin embargo utiliza una sintaxis fuertemente basada en objetos.

Referencia: http://php.net/manual/es/book.pdo.php

Conexión

La conexión a la base de datos se realiza creando un nuevo objeto de clase PDO, donde al constructor se le pasa una cadena donde se especifica con un formato como el siguiente todos los parámetros necesarios, el usuario y contraseña de conexión, y un array de atributos de la conexión. También se pueden especificar después mediante setAttribute como en el siguiente ejemplo:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Tratamiento de errores

Al conectar con la base de datos, podemos especificar que el tratamiento de errores se produzca de una de las siguientes maneras:

PDO::ERRMODE_SILENT: Similar a los modos antigüos de conexión, hay que comprobar lo devuelto en cada consulta para ver si es ===false. PDO::ERRMODE_WARNING: En caso de error se lanza un error PHP de tipo Warning PDO::ERRMODE_EXCEPTION: En caso de error se lanza una excepción.

Con este último modo, las funciones de conexión o consulta, en lugar de devolver verdadero o falso en caso de error, lanzan una excepción que puede ser capturada mediante instrucciones try-catch.

try {
    $db->query('sql invalido');
} catch(PDOException $ex) {
    echo "Ocurrió un error<br>"; //user friendly message
    echo $ex->getMessage();
}

El código dentro de la sentencia try será ejecutado con normalidad, pero si en algún momento se dispara una excepción (error) de clase PDOException, el control del programa pasará al bloque catch, almacenando en $ex el objeto excepción con metainformación del error.

En cualquier caso, podemos ignorar totalmente el tratamiento de excepciones. En este caso, al producirse alguna ocurrirá un error general de “excepción no capturada”.

Este último modo, aunque es algo más complejo de entender, permite realizar un tratamiento de errores más complejo, nos permite no tener funciones que devuelven resultados de más de un tipo a la vez (booleano false, o un array con el resultado), y es el modo estandard de tratamiento de errores que se usa en otros lenguajes fuertemente orientados a objetos como Java o C#.

Consulta de datos

Una vez hemos conectado con la base de datos, podemos empezar a realizar operaciones con ella.

La más general es la “consulta”, donde nos traemos filas de datos procedentes de una base de datos. Esto se consigue mediante la sentencia SELECT en SQL, indicando con * que queremos traernos todas las columnas, y especificando con FROM de qué tabla queremos traernos la información.

$stmt = $db->query('SELECT * FROM tabla');
foreach($stmt as $row) {
    echo $row['col1'].' '.$row['col2'];
}

Tenga en cuenta que en este caso $result no es un array, sino un objeto que implementa el interfaz iterator, por lo que la instrucción foreach utiliza los métodos especificados en dicho interfaz para recorrer todos los resultados obtenidos.

Para obtener la cuenta de cuántos resultados se han obtenido de la consulta, en lugar de contarlos directamente (pueden ser muchos), podemos preguntar directamente al resultado mediante el método rowCount(). También devolverá el número de filas afectadas en consultas de modificación o borrado.

$count = $stmt->rowCount();
echo 'Obtenidas '.$count.' filas<br>;

Parámetros

Cuando en la sentencia SQL necesitemos introducir valores parametrizados, podemos utilizar el método prepare() indicando en la consulta con el signo ? para establecer donde serán ubicados, especificando luego una lista de los mismos. El driver PDO se encargará de realizar un escapado correcto de las cadenas, de forma que se evite la posibilidad de una inyección de código SQL malintencionado.

$stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");
$stmt->bindValue(1, $id, PDO::PARAM_INT);
$stmt->bindValue(2, $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

También es posible utilizar marcadores con nombre mediante el caracter :, de manera que luego enlacemos cada nombre con el valor por el que será sustituido.

$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Es posible en ambos casos enlazar los datos en la propia sentencia execute.

$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Inserción, borrado, actualización

La inserción se realiza con la instrucción INSERT en SQL

$stmt = $db->prepare(
    "INSERT INTO table(field1,field2,field3) 
     VALUES(:field1,:field2,:field3)");
$stmt->execute( 
    array(':field1' => $field1, ':field2' => $field2, ':field3' => $field3));

El borrado de una o varias filas se realiza con la instrucción DELETE en SQL, indicando mediante WHERE cómo identificar la fila a eliminar.

$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();

Si queremos modificar el valor de una o varias columnas, de una o varias filas, se realiza mediante la instrucción UPDATE en SQL, de nuevo indicando con WHERE qué filas se verán afectadas.

$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));

Paginación

Cuando tenemos tablas con una gran cantidad de registros, es adecuado no traerselos todos con una consulta SELECT, sino realizar algún tipo de selección “página a página”. El lugar correcto para hacer esto es en las instrucciones en SQL, ya que de lo contrario aunque con PHP filtremos la información, el trabajo de consultarla y hacerla disponible a PHP ya se ha realizado.

Para ello MySQL cuenta con las instrucciones LIMIT y OFFSET, en una sintaxis SQL que no es un estandard totalmente (cada motor de base de datos utiliza instrucciones similares pero no exactamente iguales).

LIMIT nos indica cuántos registros traernos en la consulta, mientras que OFFSET nos indica a partir de qué número de registro comenzar a contar.

$stmt = $conn->prepare(
    "SELECT id,username FROM public LIMIT :limit OFFSET :offset");
$stmt->bindValue(':limit', (int) $start, PDO::PARAM_INT);
$stmt->bindValue(':offset', (int) $per_page, PDO::PARAM_INT);
$stmt->execute();

« Volver al listado completo Volver al listado de píldoras formativas »