jueves, 19 de febrero de 2015

Aprovisionar Vagrant con Ansible y Galaxy

Cuando trabajamos con Vagrant, necesitamos aprovisionar la máquina con las herramientas necesarias para cada uno de los proyectos de forma específica. Para realizar este aprovisionamiento, existen diferentes herramientas como Chef, Puppet y Ansible, por nombrar unos pocos.

Cuando aprovisionamos con Ansible, tenemos varias opciones, pero la que vamos a explicar aquí es como usarlo junto con Galaxy para obtener toda la potencia de los roles que ya tiene a disposición toda la comunidad.

Lo primero de todo y necesario es crear un Vagrantfile donde se indique que se aprovisiona la máquina con Ansible e indicarle el playbook que vamos a utilizar. Esto se hace añadiendo las siguientes lineas.
config.vm.provision 'ansible' do |ansible|
 ansible.playbook = 'ansible/site.yml'
end

Con esto ya tenemos listo Vagrant para que utilice Ansible como provisioner y además ejecute todo lo que nosotros le indiquemos en el site.yml. Un típico ejemplo de LAMP en Ansible podría ser
- hosts: all
  sudo: yes
  vars_files:
    - vars/geerlingguy.apache.yml
    - vars/geerlingguy.php.yml
    - vars/geerlingguy.php-pecl.yml
  roles:
    - { role: geerlingguy.apache }
    - { role: geerlingguy.php }
    - { role: geerlingguy.php-pecl }

Donde vemos que cargamos las distintas variables para cada uno de los roles que hemos importado y definimos los roles en si mismo. Con esto ya tendríamos un entorno con Apache y PHP, donde nos faltaría añadir algún rol de Mysql. Puede encontrar muchos en Ansible Galaxy.

El problema de este sistema radica en que los roles tiene que ser descargados manualmente desde Galaxy y eso no nos garantiza tener los roles actualizados, ni resolver las dependencias entre ellos, por lo que aquí entra al rescate librarian-ansible. Esta herramienta nos permite automatizar toda la descarga de roles desde Galaxy y así evitar el proceso manual.

Como lo único que hace es evitar el proceso manual, lo que hará será descargar los roles en una carpeta. Para eso tendremos que crear junto a nuestro site.yml un archivo llamado Ansiblefile con un contenido similar al siguiente, con los roles que más nos interesen.
#!/usr/bin/env ruby
#^syntax detection
# @see https://github.com/bcoe/librarian-ansible

site "https://galaxy.ansible.com/api/v1"

role "geerlingguy.apache"
role "geerlingguy.php-pecl"
role "geerlingguy.php"

Este archivo será revisado por librarian-ansible cada vez que queramos actualizar/instalar roles. Para instalar y actualizar los roles, solamente necesitamos ejecutar
$ librarian-ansible install

Installing geerlingguy.apache (0.0.0)
Installing geerlingguy.php (0.0.0)
Installing geerlingguy.php-pecl (0.0.0)

Con esto podemos ver que librarian nos ha creado una carpeta llamada librarian, donde se encuentran todos los roles que habíamos definido en el Ansiblefile. Además este nos ha creado un archivo llamado Ansiblefile.lock, donde se puede ver en su contenido las versiones de las librerías que tenemos instaladas y cuales son.

GIT
  remote: https://github.com/geerlingguy/ansible-role-apache
  ref: master
  sha: 2abee5155caf66c5ff53d92a46b96a604244cc42
  specs:
    geerlingguy.apache (0.0.0)

GIT
  remote: https://github.com/geerlingguy/ansible-role-php
  ref: master
  sha: ee9a827049ff6e02864679861675dd3ac6f356b5
  specs:
    geerlingguy.php (0.0.0)

GIT
  remote: https://github.com/geerlingguy/ansible-role-php-pecl
  ref: master
  sha: 4d23c5e8902b0e5de905c12ab8901c89110f141a
  specs:
    geerlingguy.php-pecl (0.0.0)

DEPENDENCIES
  geerlingguy.apache (>= 0)
  geerlingguy.php (>= 0)
  geerlingguy.php-pecl (>= 0)

Si ahora ejecutamos Ansible, veremos que ocurre un error donde se indica que los roles no existen y eso es por que por defecto Ansible busca los roles dentro de la carpeta actual, por lo que si queremos que también encuentre los que se encuentran dentro de la carpeta librarian, tendremos que indicárselo. Para ello, creamos el archivo ansible.cfg junto a nuestro Vagrantfile con el siguiente contenido
[defaults]

# additional paths to search for roles in, colon separated
roles_path = ansible/librarian                           

Con esto ya lo tendríamos todo listo para funcionar, solo necesitaremos ejecutar librarian-ansible update antes de hacer un vagrant provision para así actualizar nuestros roles.

domingo, 16 de noviembre de 2014

Mensajes con retraso en RabbitMQ

Introducción

Desde la llegada de los servicios de mensajería basados en AMQP (Advanced Message Queuing Protocol o Protocolo avanzado de colas de mensajería), la comunicación entre procesos se ha simplificado y ha permitido que ejecutar acciones asíncronas sea mucho más fácil y sencillo. En este caso vamos a tratar con RabbitMQ.

Este no es un tutorial de iniciación, pues consideraremos que el lector tiene un mínimo conocimiento del sistema de mensajería y lo tiene instalado y listo para usar.

Entrando en materia

Uno de los problemas que nos podemos encontrar con RabbitMQ es que no permite elegir cuando consumir un mensaje o si este debe ser consumido a partir de cierto momento, por lo que tenemos que emular ese comportamiento a través de los DLX (Dead Letter Exchanges). Los DLX permiten que cuando un mensaje caduque, este sea reenviardo a otro exchange. Un mensaje caduca cuando no es consumido en un TTL (tiempo de vida) especificado en el propio mensaje.

Conociendo estos procesos, podríamos crear una estructura tal y como podemos ver en la siguiente figura:
En el diagrama, podemos ver como configuramos dos exchanges. El primero de ellos Consumer Exchange, nos servirá como exchange para consumir los mensajes que queramos, por lo que ahí será donde pondremos las distintas colas junto con nuestos consumers para procesar los mensajes. Por otra parte tenemos el Delayer Exchange, donde tenemos una sola cola sin consumer, que no hará nada y servirá simplemente como repositorio donde se almacenarán los mensajes a la espera de que caduquen y ser enviados al Consumer Exchange.

Teniendo en cuenta esta estructura, podemos enviar mensajes al Delayer Exchange con un TTL para indicar en que momento será este mensaje reenviado al Consumer Exchange y así ser procesado.

Este es el código PHP para probar el sistema:

Productor

<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPConnection;
use PhpAmqpLib\Message\AMQPMessage;

/**
 * Configura tu conexión con RabbitMQ.
 */
$host  = '127.0.0.1';
$port  = 5672;
$user  = 'guest';
$password = 'guest';

$connection = new AMQPConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// Declaramos el exchange y las colas de consumo.
$channel->exchange_declare('consumer_exchange', 'fanout', false, true, false);
$channel->queue_declare('consumer_queue', false, false, false, false);
$channel->queue_bind('consumer_queue', 'consumer_exchange');

// Declaramos el exchange y las colas de retardo.
$channel->exchange_declare('delayer_exchange, 'fanout', false, true, false);
$channel->queue_declare('delayer_queue', false, false, false, false, array("x-dead-letter-exchange" => array("S", "consumer_exchange")));
$channel->queue_bind('delayer_queue', 'delayer_exchange);

// Enviamos un mensaje con un TTL de 5 segundos (5000 milisegundos).
$msg = new AMQPMessage('Fecha de creación: ' . date("Y-m-d H:i:s"), array("x-expires" => 5000)));
$channel->basic_publish($msg, 'delayer_exchange');
echo "Mensaje enviado";

$channel->close();
$connection->close();
?>

Consumidor

<?php
/**
 * Consumidor de mensajes.
 */
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPConnection;

/**
 * Configura tu conexión con RabbitMQ.
 */
$host  = '127.0.0.1';
$port  = 5672;
$user  = 'guest';
$password = 'guest';

$connection = new AMQPConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// Declaramos el exchange y la cola de consumo.
$channel->exchange_declare('consumer_exchange', 'fanout', false, true, false);
$channel->queue_declare('consumer_queue', false, false, false, false);
$channel->queue_bind('consumer_queue', 'consumer_exchange');

// Función procesadora de mensajes.
$callback = function($msg) {
 echo "La diferencia entre " . date("Y-m-d H:i:s") . " y " . $msg->body . " no debería ser nunca inferior a 5 segundos.\n";

 // Liberamos el mensaje
 $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
$channel->basic_consume('consumer_queue', '', false, true, false, false, $callback);

while(count($channel->callbacks)) {
 $channel->wait();
}

$channel->close();
$connection->close();
Este sistema se puede utilizar también para hacer reintentos espaciados en el tiempo, haciendo que el mensaje se reenvíe al Delayer Exchange en caso de que en ese momento no se pueda procesar el mensaje y se quiera hacer en un futuro.

Conclusión

Con este sistema tenemos la ventaja de olvidarnos de procesos activos de espera y evitar malgastar recursos del sistema, lo cual puede llegar a bloquear otros mensajes. Por otra parte, este sistema no asegura que el mensaje sea consumido en un tiempo exacto, y solo permite saber a partir de cuando será consumido (TTL).

Para quien necesite realizar esto con diversas colas y no quiera tener infinidad de exchanges, puede modificar el Delayer Exchange para que sea de tipo topic o headers y así definir los distintos DLX a nivel de cola.

domingo, 6 de mayo de 2012

Tests unitarios en PHP

Los tests en un proyecto sirven para poder "asegurarnos" de si la aplicación está funcionando correctamente pese a haber echo una modificación/refactorización. Esto nos permite evitar regresiones en el código o si las tenemos detectarlas fácilmente y encontrar rápidamente una solución.

En este articulo hablaremos sobre que son los tests unitarios y veremos un ejemplo sencillo de como funcionan.

Tests unitarios

Los tests unitarios, como su propio nombre indica, se basan en comprobar si una unidad de código funciona de la forma que nosotros esperamos. Normalmente la unidad de código se considera la clase, por lo que los tests unitarios se dedican a probar a fondo una sola clase de forma que sepamos que esa clase funciona como esperamos.

Un ejemplo de test unitario podría ser:
<?php
 class Calculadora{
  public function suma( $a, $b )
  {
   return $a + $b;
  }
 }

 $calculadora = new Calculadora();
 if( $calculadora->suma(1,2) === 3 )
 {
  echo 'Test1. Resultado correcto.';
 }
 else
 {
  echo 'Test1. Resultado incorrecto.';
 }
?>

Como puede verse en el ejemplo, únicamente prueba la clase Calculadora y prueba que dadas unas entradas se produce la salida esperada. Es muy importante que el test no tenga ningún tipo de lógica y los resultados esperados no sean calculados, ya que un test que realiza las mismas operaciones que lo que está probando no es un test.

Esto sería un ejemplo de un test incorrecto:
<?php
 class Calculadora{
  public function suma( $a, $b )
  {
   return $a + $b;
  }
 }

 $calculadora = new Calculadora();
 for ( $a = 0; $a < 10; ++$a )
 {
  for ( $b = 0; $b < 10; ++$b )
  {
   if ( $calculadora->suma( $a, $b ) === $a + $b )
   {
    echo "Test $a+$b correcto";
   }
   else
   {
    echo "Test $a+$b incorrecto";
   }
  }
 }
?>

Como puede verse el código tiene mucha lógica, teniendo dos bucles y operaciones algorítmicas. Si analizas el código te das cuenta de que este test no está probando nada, ya que el test realiza la misma operación que la función probada ( $a + $ b ). Eso significa que si la función probada está mal, también lo estará la del test y el error no se detectará. Por eso siempre que hagas un test, debes hacer los cálculos a mano y establecer que en dichas entradas el resultado debe ser el que tu has calculado.

Estos dos ejemplos, son muy sencillos y esto casi nunca se da en un entorno real. Aún pese a ser sencillos escribir el test ha sido engorroso y además se sitúa en el mismo archivo que la clase, por lo que este archivo no puede ir a producción. Para facilitarnos la tarea en la creación de tests y hacerlos la vida más sencilla, existen los frameworks para tests, que es lo que utilizaremos a partir de ahora.

Tests unitarios en PHP con PHPUnit

En PHP, el estándar para la realizar los tests es el framework PHPUnit. Este framework nos proporciona todo lo necesario para poder realizar los tests que nos dirán si nuestra aplicación está funcionando como nosotros esperamos de una forma mucho más sencilla que la vista anteriormente.

Para ver el funcionamiento de PHPUnit, nada mejor que ver un ejemplo y explicar paso a paso como funciona. Os recuerdo que este será un ejemplo sencillo y lo iremos complicando en los siguientes artículos.

Para empezar, tenemos nuestra clase calculadora, que se almacenará en un archivo llamado "calculadora.php":
<?php
 //calculadora.php
 class Calculadora{
  public function suma( $a, $b )
  {
   return $a + $b;
  }
 }
?>

Ahora que tenemos nuestra clase, vamos a probarla con PHPUnit, el código para probar esta clase sería el siguiente:
<?php
 //CalculadoraTest.php
 require_once( 'calculadora.php' );

        /**
         * Los nombres de las clases tiene que terminar por *Test y el nombre del archivo debe ser el mismo
         * que el de la clase.
         */
        class CalculadoraTest extends PHPUnit_Framework_TestCase{
                /**
                 * Objeto a probar, en nuestro caso la calculadora.
                 *
                 * @var Calculadora
                 */
                protected $obj;

                /**
                 * Este método es ejecutado antes de cada ejecución de test, de forma
                 * que todo lo que tengas que inicializar en todos los tests, lo puedes
                 * centralizar aquí.
                 */
                public function setUp()
                {
                        $this->obj = new Calculadora();
                }

                /**
                 * Este método se ejecuta después de cada test ejecutado, por lo que
                 * se dedicará a limpiar los restos de la ejecución del test.
                 */
                public function tearDown()
                {
                        $this->obj = null;
                }

                /**
                 * Test para comprobar la funcionalidad del objeto
                 */
                public function testSuma()
                {
                        $this->assertEquals( 3, $this->obj->suma( 1, 2 ), 'El resultado de 1 + 2 debe ser 3.' );
                }
        }
?>

El nombre de la clase de testing debe terminar por Test y el mismo nombre de la clase debe ser el mismo nombre del archivo. Esto lo utiliza PHPUnit para buscar las clases a ejecutar y saber que nombre tienen. Además esta clase extiende de PHPUnit_Framework_TestCase, que es la clase que nos aportará todas las herramientas necesarias para el test.

Una vez entramos dentro de la clase, vemos un par de métodos mágicos llamados setUp y tearDown. Este par de métodos son opcionales y se dedican a inicializar y limpiar respectivamente los tests. Esto nos permitirá que todas las acciones repetitivas de dentro de los tests, extraerlas y que los tests solo contengan realmente la lógica de comprobación. Además esto nos permite respetar otra regla importante de los tests y es que un test no puede ser nunca dependiente de otro, por lo que el orden de ejecución de los tests nunca debería de importar, y es por esa razón que el tearDown borra el objeto creado para el test anterior, por si este ha hecho alguna modificación al objeto que podría hacer fallar al siguiente test.

Todos los métodos que empiecen por la palabra test son los que se van a ejecutar y deben comprobar el funcionamiento de la clase deseada. En nuestro caso, el método testSuma comprueba que si sumo 1 y 2, el resultado sea 3. Como podeis ver, la forma en que se comprueba si todo a ido bien es con la función assertEquals.

Las función assertEquals lo que permite es que dado el primer parámetro, que es lo que espero como resultado y un segundo parámetro donde indico el resultado real de la operación, saber si los resultados coinciden. Además a esta función se le puede pasar un tercer parametro para que en el caso de que la comparación falle, me muestre dicho mensaje de error. Como puedes ver es mucho más sencillo usar esta simple función que la lógica if/else utilizado en nuestro primer ejemplo sin framework.

A estas comparaciones para saber si algo ha ido bien, se le llaman aserciones y assertEquals es solo una de las muchas que tiene PHPUnit para poder comprobar que un resultado ha sido correcto. En el framework tienes disponibles desde aserciones para comprobar si un número es mayor que otro, hasta comprobar si el atributo de un objeto es igual a lo que tu quieres, pasando por saber si un array tiene un cierta clave. Os aconsejo darle una mirada a la gran cantidad de aserciones de las que dispone en su manual.

Finalmente para ejecutar los tests de phpunit, es tan sencillo como ejecutar la orden:
$phpunit --colors CalculadoraTest.php

Esto nos mostrará un resultado, que será verde en caso de que todo haya ido bien y rojo en caso contrario.

Os dejo los archivos del articulo adjuntos en el mismo y en los siguientes artículos veremos más a fondo el funcionamiento del framework y como abordar casos más complejos.

Referencias

  1. PHPUnit

Descargar scripts del artículo.

domingo, 18 de marzo de 2012

Como utilizar APC

¿Que es APC?

APC es un sistema de cache NO distribuido, que nos permite almacenar en memoria RAM los datos que necesitamos, además de un acelerador de PHP, por lo que también aumentará el rendimiento de nuestros scripts.

¿Como funciona APC?

APC una vez instalado, puede tener dos usos:
Acelerador de PHP
PHP es un lenguaje interpretado, por lo que no es el sistema operativo quien arranca nuestros scripts, si no que es PHP el que se encarga de interpretar lo que hemos escrito e interpretarlo generando su propio código máquina (opcode).
Ejecutando un script sin APC Cada vez que se ejecuta un script, sucede lo que se puede ver en la imagen superior. Si os fijáis, en un entorno de producción el paso de compilar el script no es necesario, ya que sabemos que ese archivo no va a cambiar, por lo que nos podríamos ahorrar la compilación del código y obtener siempre el mismo opcode compilado la primera vez. En este punto es donde entra APC. APC lo que hace por defecto es almacenar el opcode generado por el PHP y cuando PHP pide nuevamente el archivo, en vez de tenerlo que compilar de nuevo, APC se lo sirve directamente ahorrándonos todo el proceso de compilación, ahorrando así CPU y aumentando la velocidad de ejecución y respuesta de nuestros scripts.
Ejecutando un script con APC Por defecto APC cada vez que PHP le pide el opcode de un archivo el comprueba si el archivo ha sido modificado desde la última vez que obtuvo su opcode. De esta forma este sistema nos puede servir también en un entorno de desarrollo, ya que no tenemos que ir eliminando manualmente el opcode de la versión anterior de nuestro script. Esto nos trae un inconveniente, y es que como se puede ver en el gráfico, APC necesita realizar por cada petición un acceso a disco para comprobar la fecha de modificación del archivo y pese a que aún así es más rápido que compilar de nuevo nuestro script, es una operación completamente innecesaria en un entorno de producción donde sabes que los archivos no van a cambiar, y en caso de cambiar puedes hacer manualmente una limpieza de caché. Por eso APC nos provee del flag "stat", representado también en el flujo, que nos permite activar o desactivar ese comportamiento.

Sistema de caché
Desde PHP puedes decirle a APC que almacene un valor en memoria RAM, de tal forma que cuando queramos podemos volver a obtener ese dato de forma muy rápida. Esto puede servir por ejemplo para almacenar un resultado de una consulta que sea idéntica a todos los usuarios y que no queramos repetirla por cada conexión. De esta forma el primer usuario que ejecutaría la página haría la consulta a base de datos y almacenaría el resultado en APC. A partir de ese momento, todos los usuarios que accedieran al sitio no necesitarían obtener el resultado de base de datos, si no que lo obtendrían directamente de memoria RAM, por lo que la obtención del dato será muchísimo más rápida que obtenerla desde Mysql.

¿Como se usa?

Apc es una extensión de PHP, por lo que instalarla es tan sencillo como configurarla. Si tienes un sistema operativo linux basado en debian, es algo ran sencillo como:
$ sudo apt-get install php-apc
Para usarla, disponemos de una API que se puede consultar en la documentación oficial de PHP y que permite básicamente almacenar datos en memoria y obtenerlos. El funcionamiento es muy sencillo, ya que de forma básica solo necesitas utilizar las funciones apc_fetch para la recuperación de datos y apc_store para el almacenamiento de datos.
Os dejo un ejemplo para que veáis un uso muy básico, pero que cubre la gran mayoría de los casos
<?php
// Aquí solo se accede la primera vez
if ( !apc_exists( 'calculo_super_complicado' ) 
{
 $resultado = hacerCalculoMuyLentoYComplicado();
 apc_store( 'calculo_super_complicado', $resultado );
 echo $resultado;
}
//El resto de usuarios ejecutan esta parte
else 
{
 echo apc_fetch( 'calculo_super_complicado' );
}
?>

Al usar APC como sistema de caché, se tiene que tener en cuenta que por defecto APC tiene configurado un tamaño de caché de 32mb, por lo que cuando llegue a ese límite automáticamente las entradas menos usadas se irán descartando a medida que se añadan de nuevas. Si deseas borrar algo manualmente, solo tienes que usar la instrucción apc_delete y si quieres ampliar el tamaño de almacenamiento, solo debes modificar la configuración correspondiente.

Gestión de APC

APC dispone además de un entorno web muy útil para ver información sobre el uso que está teniendo el sistema de caché. Para poderlo usar simplemente debes copiar el archivo apc.php a una carpeta que apache pueda tener acceso. El usuario y contraseña para acceder a la página se encuentra dentro del propio archivo php, por lo que es muy fácil de editar y poner los que más te convengan.
Os dejo unas captura de como es actualmente este panel.
Esto ha sido un acercamiento a los sistemas de cache, el opcode y concretamente a APC. Existen otros sistemas de caché y APC puede hacer más de lo explicado en esta entrada, pero se han cubierto sus características básicas.

Actualización:
El archivo apc.php, puede encontrar en las distribuciones ubuntu en la ruta "/usr/share/doc/php-apc/apc.php.gz". Solo se tiene que descomprimir y listo.



miércoles, 14 de marzo de 2012

Patrones de diseño IV - Inyección de dependencias


En la anterior entrada, vimos como crear un adaptador, pero no llegamos a ver como podemos utilizar esos adaptadores para sacar toda su utilidad. Para eso vamos a ver el patrón de inyección de dependencias.

Este patrón nos ayudará a que nuestra aplicación tenga un bajo acoplamiento y una alta cohesión. Para los que no estén familiarizados, decir que eso significa que dos piezas de software no deben ser dependientes entre ellas, pero deben definir correctamente una unidad de software. Para más detalles os dejo una referencia al final de la entrada(1).

Si programas una clase sin utilizar este patrón, la clase resultante puede ser parecida a la del ejemplo, donde se crean objetos dentro de la propia clase y eso hace que haya un fuerte acoplamiento, que es algo que queremos evitar.
class my_class
{
 public function obtenerDatos()
 {
  $base_de_datos = new Mysqli(); //Fuerte acoplamiento

  $base_de_datos->connect();
  $result = $base_de_datos->query( 'SELECT * FROM tabla' );

  return $result;
 }
}

$instancia = new my_class(); 
$instancia->obtenerDatos();

Si utilizamos el patrón, la dependencia se inyecta de forma externa, por lo que la propia clase ya no necesita saber que objeto va a utilizar (realizar un new Objeto), si no solamente la interfaz que va a utilizar de ese objeto, y ahí es cuando entran en juego los adaptadores.

Los adaptadores, tal y como explicamos en la anterior entrada, tienen una interfaz en común que nos asegura que esos objetos tienen un conjunto común de métodos. De esta forma se puede inyectar en la clase cualquier objeto que posea la interfaz que el requiera y olvidarse de que objeto concreto usa.
class my_class
{
 //Inyección de un objeto del tipo InterfazBaseDeDatos
 public function obtenerDatos( InterfazBaseDeDatos $base_de_datos )
 {
  $base_de_datos->connect();
  $result = $base_de_datos->query( 'SELECT * FROM tabla' );

  return $result;
 }
}

$base_de_datos = new AdaptadorMysqli(); //Creamos la dependencia de forma externa.
$instancia = new my_class(); 
$instancia->obtenerDatos( $base_de_datos ); //Inyectamos la dependencia.

En el ejemplo puede verse que la clase ya no requiere el objeto Mysqli, si no que requiere un objeto que tenga la interfaz InterfazBaseDeDatos. Esto permitirá nuestra clase utilizar cualquier objeto que disponga del adaptador.

La dependencia, en el ejemplo anterior, se ha inyectado en el mismo método en el que se utiliza la dependencia externa, pero la inyección de dependencia se puede realizar en otros lugares como por ejemplo el constructor o un método específico para la inyección de dependencias.

Tipos de inyecciones alternativas


Constructor

//Inyección de dependencia en el constructor.
class my_class
{
 //Código de la clase
 public function __construct( InterfazBaseDeDatos $base_de_datos )
 {
  $this->base_de_datos = $base_de_datos;
 }
 //Código de la clase

}

Método específico

//Inyección de dependencia en un método concreto.
class my_class
{
 //Código de la clase
 public function establecerConectorBaseDeDatos( InterfazBaseDeDatos $base_de_datos )
 {
  $this->base_de_datos = $base_de_datos;
 }
 //Código de la clase

}

Por último decir, que si leéis más cosas sobre inyección de dependencias y os encontráis con algo llamado "Contenedor de inyección de dependencias", decir que no es lo mismo que explicado en esta entrada.

Referencias

  1. Acoplamiento y Cohesión

lunes, 5 de marzo de 2012

Patrones de diseño III - Adaptadores

Los adaptadores son un tipo de clase que nos permiten unificar una interfaz para poder cambiar fácilmente entre distintas librerías, haciéndolas así compatibles con nuestra aplicación.

Para ejemplificar este patrón, consideraremos que tenemos una aplicación con la que queremos tener acceso a base de datos con el conector Mysqli, pero queremos dejar la aplicación preparada para poder cambiar de conector fácilmente.

Para empezar a realizar un adaptador, lo primero que tenemos que definir es un interfaz. Pese a que no es un requisito obligatorio, definir un interfaz hará que sea más fácil la implementación del adaptador, evitando así muchos problemas. Para quien no sepa lo que es una interfaz (interface en inglés), le aconsejo que se lea la documentación oficial de php sobre interfaces.

La interfaz que definiremos será un método para poder conectar a la base de datos y otro método para poder ejecutar las consultas y que además nos devuelva su resultado. Un posible código para la interfaz podría ser el siguiente:
<?php
/**
 * Interfaz para la conexión a una base de datos
 */
interface conexionBd
{
        /**
         * Permite al usuario conectarse a la base de datos indicada.
         *
         * @param string $servidor Dirección del servidor.
         * @param string $usuario Nombre de usuario.
         * @param string $contrasena Contraseña.
         * @param string $base_de_datos Nombre de la base de datos a conectar.
         * @return boolean Devuelve TRUE si todo ha ido bien, si no FALSE.
         */
        public function conectar( $servidor, $usuario, $contrasena, $nombre_base_de_datos );

        /**
         * Realiza una petición al motor sql y devuelve el resultado
         * de su ejecución.
         *
         * @param string $sql Petición SQL.
         * @return mixed
         */
        public function peticion( $sql );
}
?>

Como puedes ver, aquí no definimos ningún tipo de implementación, por lo que los métodos están vacíos. Será la clase que implemente esta interfaz, la que deberá programar su lógica.

Ahora que ya tenemos una interfaz común para el acceso a todo tipo de conectores de bases de datos, vamos a crear una clase adaptador que adapte el objeto Mysqli original a  la interfaz genérica que hemos definido.

El código de nuestra clase adaptador es:
<?php
/**
 * Adaptador para Mysqli
 */
class AdaptadorMysqli implements conexionBd
{
        /**
         * Conexión a la base de datos.
         *
         * @var Mysqli
         */
        protected $conexion;

        /**
         * Permite al usuario conectarse a la base de datos indicada.
         *
         * @param string $servidor Dirección del servidor.
         * @param string $usuario Nombre de usuario.
         * @param string $contrasena Contraseña.
         * @param string $base_de_datos Nombre de la base de datos a conectar.
         * @return boolean Devuelve TRUE si todo ha ido bien, si no FALSE.
         */
        public function conectar( $servidor, $usuario, $contrasena, $nombre_base_de_datos )
        {
                $this->conexion = new Mysqli( $servidor, $usuario, $contrasena, $nombre_base_de_datos );
        }

        /**
         * Realiza una petición al motor sql y devuelve el resultado
         * de su ejecución.
         *
         * @param string $sql Petición SQL.
         * @return mixed
         */
        public function peticion( $sql )
        {
                $resultado = $this->conexion->query( $sql );
                if ( $resultado->num_rows > 0 )
                {
                        $datos = array();
                        while( $row = $resultado>fetch_assoc() )
                        {
                                $datos[] = $row;
                        }

                        return $datos;
                }

                return $resultado;
        }

}
?>


Como puedes ver lo primero que hace el código es decir que la clase que implementa la interfaz que hemos definido.
class AdaptadorMysqli implements conexionBd 
De esta forma ahora PHP nos obligará a crear en nuestra clase  todas las funciones que se indiquen en la interfaz que implementa.

En él método de conectar hemos implementado como se realiza la conexión a Mysqli, mientras que en él método de petición hemos implementado una petición más una obtención de resultados. Como ves, no es necesario, ni práctico, que la interfaz se adapte a Mysqli, si no que es al revés, Mysqli debe adaptarse a la interfaz, ya que ahora mismo es Mysqli el que es adaptado, pero al día siguiente puede ser PDO el que quieras adaptar y la misma interfaz te debe servir para cualquiera.

Con todo esto, ya hemos terminado nuestro adaptador pero, ¿esto que nos aporta?. Ahora podemos utilizar en toda nuestra aplicación objetos del tipo conexionBd sin que nos importe que conector a base de datos haya detrás, ya que todos se accederán de la misma forma por la aplicación.

Os dejo el código completo del ejemplo para descargar.

miércoles, 29 de febrero de 2012

Patrones de diseño II - Singleton

Aquí venimos de nuevo con un nuevo patrón de diseño, el denominado singleton. Este patrón nos servirá para cuando queremos que un objeto tenga una sola y única instancia en toda la aplicación, como por ejemplo al conexión a la base de datos.
La conexión a bases de datos consume muchos recursos del servidor, por lo que nos interesa, conectar una sola vez y realizar todas las consultas necesarias, además de esta forma, nos evitamos tener que crear múltiples objetos que también hacen que la aplicación ocupe más memoria.

A continuación podéis ver la estructura básica de un singleton y después lo explicaremos detalladamente.

/**
 * Objeto del cual deseamos tener una sola instancia
 * en toda la aplicación.
 */
class mi_objeto
{
        /**
         * Aquí se almacenará la única instancia existente
         * del propio objeto.
         *
         * @var mi_objeto
         */
        private static $instancia;

        /**
         * Constructor para la creación de una instancia
         * del objeto.
         */
        private function __construct(){}

        /**
         * Método que siempre devuelve la misma instancia
         * del objeto.
         */
        public static function getInstance()
        {
                if ( !self::$instancia )
                {
                        self::$instancia = new self;
                }

                return self::$instancia;
        }
}


La primera parte y fundamental del código es la variable $instancia.
private static $instancia;
Esta variable almacenará la única instancia disponible en toda la aplicación. Esta variable es estática, ya que debe estar siempre accesible. Si no sabéis que es o para que sirve una variable estática, os recomiendo ver los ejemplos disponibles en la documentación oficial de php.

Más adelante nos encontramos con algo que al principio parece extraño y es que el constructor de la clase es privado. Como hemos dicho solo queremos una instancia en toda la aplicación y es por eso que el constructor es privado, para que nadie pueda realizar por desconocimiento o despiste algo como
$variable = new mi_objeto();
ya que al ser el constructor privado, esto lanzaría un error en la aplicación y evitaría la creación descontrolada de objetos.

Llegados a este punto muchos se preguntarán como se hace entonces para poder crear una un objeto, teniendo el operador new bloqueado. Pues la respuesta es sencilla como ejecutar el método getInstance.
$obj = mi_objeto::getInstance();
Como puede verse en el código del método getInstance, lo que hace es primero que todo comprobar si el objeto ha sido creado anteriormente y en caso de que no haya sido creado anteriormente lo crea y lo devuelve. A partir de este momento, todas las veces que queramos obtener un objeto de la clase mi_objeto, ya no se devolverá una nueva instancia, si no que se obtendrá la generada por primera vez.

A continuación dejo un ejemplo práctico de una clase singleton, con un ejemplo de su funcionamiento. Probad a jugar un poco con ella y pensar posibles usos.
/**
 * Objeto del cual deseamos tener una sola instancia
 * en toda la aplicación.
 */
class mi_objeto
{
        /**
         * Aquí se almacenará la única instancia existente
         * del propio objeto.
         *
         * @var mi_objeto
         */
        private static $instancia;

        /**
         * Variable para almacenar un número
         */
        public $numero;

        /**
         * Constructor para la creación de una instancia
         * del objeto.
         */
        private function __construct(){}

        /**
         * Método que siempre devuelve la misma instancia
         * del objeto.
         */
        public static function getInstance()
        {
                if ( !self::$instancia )
                {
                        self::$instancia = new self;
                }

                return self::$instancia;
        }
}

//Creo un objeto y le asigno un numero
$var = mi_objeto::getInstance();
$var->numero = 23;

//Elimino el contenido de la varible.
$var = null;

//Vuelvo a obtener una instancia del objeto.
//Al ser un singleton lo que se obtendrá es la
//misma instancia que estaba en $var
$nueva_var = mi_objeto::getInstance();

//Imprimirá el número 23
echo $nueva_var->numero;

Os dejo tanto el código base del singleton como del ejemplo para su descarga.