PDO (PHP Data Objects). Capa de Abstracción de acceso a Bases de Datos utilizando el Patrón de diseño Singleton (Segunda parte)

Como crear una Capa de Abstracción de acceso a Bases de Datos utilizando la extensión PDO y el Patrón de diseño Singleton.

Es decir, crearemos una clase Database que usara internamente PDO y el Patrón Singleton.







Ejemplo

Database.php

  1. <?php  
  2. require_once 'web.config.php';  
  3.   
  4. final class Database  
  5. {  
  6.   private static $dns       = DNS;  
  7.   private static $username  = USERNAME;  
  8.   private static $password  = PASSWORD;  
  9.   private static $instance;  
  10.       
  11.   private function __construct() { }  
  12.       
  13.   /** 
  14.    * Crea una instancia de la clase PDO 
  15.    *  
  16.    * @access public static 
  17.    * @return object de la clase PDO 
  18.    */  
  19.   public static function getInstance()  
  20.   {  
  21.     if (!isset(self::$instance))  
  22.     {  
  23.       self::$instance = new PDO(self::$dns, self::$username, self::$password);  
  24.       self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  
  25.     }  
  26.     return self::$instance;  
  27.   }  
  28.       
  29.       
  30.  /** 
  31.   * Impide que la clase sea clonada 
  32.   *  
  33.   * @access public 
  34.   * @return string trigger_error 
  35.   */  
  36.   public function __clone()  
  37.   {  
  38.     trigger_error('Clone is not allowed.', E_USER_ERROR);  
  39.   }  
  40. }  
  41. ?>  

web.config.php

  1. <?php  
  2. define('DNS','mysql:dbname=midbname;host=mihost');  
  3. define('USERNAME','miusername');  
  4. define('PASSWORD','mipassword');  
  5. ?>  

test.php

  1. <?php  
  2. /* 1. Conexión a la base de datos */  
  3.   
  4. require_once 'Database.php';  
  5. $dbh = Database::getInstance();  
  6.   
  7. /* Creación de la base de datos noticias */  
  8.   
  9. $dbh->query('CREATE DATABASE `noticias`  
  10.              DEFAULT CHARACTER SET  
  11.              latin1 COLLATE latin1_swedish_ci');  
  12. $dbh->query('USE `noticias`');  
  13.   
  14. /* Creación la tabla noticias */  
  15.   
  16. $dbh->query('CREATE TABLE IF NOT EXISTS `noticia` (  
  17.             `id` int(11) NOT NULL AUTO_INCREMENT,  
  18.             `titulo` varchar(50) NOT NULL,  
  19.             `texto` varchar(500) NOT NULL,  
  20.              PRIMARY KEY (`id`)  
  21.             )  
  22.             ENGINE=InnoDB  
  23.             DEFAULT CHARSET=utf8 AUTO_INCREMENT=2');  
  24.   
  25. /* Insertando datos en la tabla noticias */  
  26.   
  27. $dbh->query("INSERT INTO `noticia` (`titulo`, `texto`)  
  28.              VALUES ('PHP5 ', 'PHP5 y la POO.')");  
  29.   
  30. /* 2. Enviar la instrucción SQL a la base de datos */  
  31.   
  32. $resulset = $dbh->query('SELECT   `titulo`,`texto`   
  33.                          FROM     `noticia`  
  34.                          ORDER BY `titulo`');  
  35.   
  36. /* 3. Obtener y procesar los resultados */  
  37.   
  38. $noticia = $resulset->fetchObject();  
  39. echo $noticia->titulo;  
  40. echo $noticia->texto;  
  41. ?>  

Artículos relacionados

PDO (PHP Data Objects). Capa de Abstracción de acceso a Bases de Datos (Primera parte)

PDO (PHP Data Objects) es una extensión nativa de PHP5, consiste en una interface uniforme para acceder a varias bases de datos. PDO provee una capa de abstracción de acceso a bases de datos (BD), que permite al desarrollador abstraerse de la BD de una aplicación. Así, si en alguna fase de desarrollo del proyecto se necesita cambiar de BD, esto no afectaría la lógica de la aplicación.


 


Actualmente los siguientes drivers implementan la Interface PDO:

Nombre del Driver --------- Base de Datos soportadas
PDO_DBLIB ------------------ FreeTDS / Microsoft SQL Server / Sybase
PDO_FIREBIRD -------------- Firebird/Interbase 6
PDO_IBM --------------------- IBM DB2
PDO_INFORMIX ------------- IBM Informix Dynamic Server
PDO_MYSQL ----------------- MySQL 3.x/4.x/5.x
PDO_OCI ---------------------- Oracle Call Interface
PDO_ODBC ------------------- ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
PDO_PGSQL ------------------ PostgreSQL
PDO_SQLITE ----------------- SQLite 3 and SQLite 2
PDO_4D ---------------------- 4D

Acceso a bases de datos en PHP5 con PDO

La extensión php_pdo para el trabajo con bases de datos esta compuesta por tres clases:

PDO: Representa una conexión entre PHP y un servidor de bases de datos.
PDOStatement: Representa una instrucción preparada y después que la instrucción es ejecutada, una result set.
PDOException: Representa un error lanzado por PDO.

La clase PDO

Sinopsis
2  PDO {
3  __construct ( string $dsn [, string $username
[, string $password [, array $driver_options ]]] )
4  bool          beginTransaction ( void )
5  bool          commit ( void )
6  mixed         errorCode ( void )
7  array         errorInfo ( void )
8  int           exec ( string $statement )
9  mixed         getAttribute ( int $attribute )
10  array        getAvailableDrivers ( void )
11  string       lastInsertId ([ string $name = NULL ] )
12  PDOStatement prepare ( string $statement 
[, array $driver_options = array() ] )
13  PDOStatement query ( string $statement )
14  string       quote ( string $string 
[, int $parameter_type = PDO::PARAM_STR ] )
15  bool         rollBack ( void )
16  bool         setAttribute ( int $attribute , mixed $value )
17  }
Métodos
  1. PDO::beginTransaction — Inicializa una transacción.
  2. PDO::commit — Commits una transacción.
  3. PDO::__construct — Crea una instancia PDO que representa una conexión a una base de datos.
  4. PDO::errorCode — Recupera el SQLSTATE asociado con la última operación en la base de datos.
  5. PDO::errorInfo — Recupera información extendida del error asociado con la ultima operación en la base de datos.
  6. PDO::exec — Ejecuta una instrucción SQL y retorna el número de filas afectadas.
  7. PDO::getAttribute — Recupera un atributo de conexión a base de dato.
  8. PDO::getAvailableDrivers — Retorna un array (arreglo) de los drivers disponibles en la extensión PDO.
  9. PDO::lastInsertId — Retorna el ID (identificador) de la última fila insertada o secuencia de valores.
  10. PDO::prepare — Prepara una instrucción para ejecución y retorna un objeto de tipo PDOStatement.
  11. PDO::query — Ejecuta una instrucción SQL, retornando un resul set como un objeto de tipo PDOStatement.
  12. PDO::quote — Quotes (Pone entre comillas simples un string) para uso en una query (consulta).
  13. PDO::rollBack — Rolls back una transacción.
  14. PDO::setAttribute — Modifica un atributo.
La clase PDOStatement

Sinopsis

2  PDOStatement implements Traversable {
3  bool    bindColumn ( mixed $column , mixed &$param 
[, int $type [, int $maxlen [, mixed $driverdata ]]] )
4  bool    bindParam ( mixed $parameter , mixed &$variable 
[, int $data_type [, int $length
[, mixed $driver_options ]]] )
5  bool    bindValue ( mixed $parameter , mixed $value 
[, int $data_type ] )
6  bool    closeCursor ( void )
7  int     columnCount ( void )
8  bool    debugDumpParams ( void )
9  string  errorCode ( void )
10 array   errorInfo ( void )
11 bool    execute ([ array $input_parameters = array() ] )
12 mixed   fetch ([ int $fetch_style = PDO::FETCH_BOTH
      [, int $cursor_orientation = PDO::FETCH_ORI_NEXT 
[, int $cursor_offset = 0 ]]] )
13 array   fetchAll ([ int $fetch_style = PDO::FETCH_BOTH 
[, int $column_index [, array $ctor_args = array() ]]] )
14  string fetchColumn ([ int $column_number = 0 ] )
15  mixed  fetchObject ([ string $class_name 
[, array $ctor_args ]] )
16  mixed  getAttribute ( int $attribute )
17  array  getColumnMeta ( int $column )
18  bool   nextRowset ( void )
19  int    rowCount ( void )
20  bool   setAttribute ( int $attribute , mixed $value )
21  bool   setFetchMode ( int $mode )
22  }
Métodos
  1. PDOStatement->bindColumn — Sustituye el valor de una columna de la base de datos a una variable PHP.
  2. PDOStatement->bindParam — Sustituye el valor de un parámetro (argumento), a el nombre de variable especificada.
  3. PDOStatement->bindValue — Sustituye un valor como parámetro.
  4. PDOStatement->closeCursor — Cierra el cursor, habilitando la instrucción a ser ejecutada otra vez.
  5. PDOStatement->columnCount — Retorna el numero de columnas en el result set.
  6. PDOStatement->debugDumpParams — Dump un commando SQL preparado.
  7. PDOStatement->errorCode — Recupera el SQLSTATE asociado con la última operación realizada en la base de datos.
  8. PDOStatement->errorInfo — Recupera información de error extendida asociada con la ultima operación realizada en la base de datos.
  9. PDOStatement->execute — Ejecuta una instrucción preparada.
  10. PDOStatement->fetch — Recupera la siguiente fila de una result set.
  11. PDOStatement->fetchAll — Retorna un array conteniendo todas las filas del resul set.
  12. PDOStatement->fetchColumn — Retorna una única columna de la siguiente fila de un result set.
  13. PDOStatement->fetchObject — Recupera la siguiente fila y la retorna como un objeto.
  14. PDOStatement->getAttribute — Recupera una atributo.
  15. PDOStatement->getColumnMeta — Retorna metadatos de una columna en un resul set.
  16. PDOStatement->nextRowset — Avanza a la siguiente Rowset en un multi-rowset.
  17. PDOStatement->rowCount — Retorna el numero de filas afectadas por la última instrucción SQL.
  18. PDOStatement->setAttribute — Modifica un atributo.
  19. PDOStatement->setFetchMode — Modifica el valor por defecto para recuperar datos de la base de datos para la instrucción actual.
Los pasos básicos para interactuar con una base de datos desde PHP son los siguientes:

1. Conectar con el servidor de bases de datos.
2. Enviar la instrucción SQL a la base de datos.
3. Obtener y procesar los resultados.

A continuación vemos los métodos concretos con los que realizaremos estas operaciones:

1. Conectar con el servidor de bases de datos (Crear el objeto PDO):

Descripción
PDO::__construct ( string $dsn [, string $username 
[, string $password [, array $driver_options ]]] )

Lista de parámetros

DNS: (Data Source Name), contiene la información requerida para conectarse a la base de datos.
username: El nombre de usuario. Este parámetro es opcional para algunos PDO drivers .
password: La contraseña. Este parámetro es opcional para algunos PDO drivers.
driver_options: Una llave=>valor del arreglo de opciones con driver de conexión especifico.

Valores retornados

En caso de éxito retorna un objeto PDO.

Errores/Excepciones

PDO::__construct() dispara un PDOException si el intento de conexión a la base de datos falla.

2. Enviar la instrucción SQL a la base de datos:

Descripción

PDOStatement PDO::query ( string $statement )

PDOStatement PDO::query ( string $statement , 
int $PDO::FETCH_COLUMN , 
int $colno )

PDOStatement PDO::query ( string $statement , 
int $PDO::FETCH_CLASS,
  string $classname ,
array $ctorargs )

PDOStatement PDO::query ( string $statement , 
int $PDO::FETCH_INTO ,
object $object )

Lista de parámetros

Instrucción SQL.

Valores retornados

PDO::query()
retorna un objeto PDOStatement.

3. Obtener y procesar los resultados.

Descripción
mixed PDOStatement::fetchObject ([ string $class_name 
[, array $ctor_args ]] )

Lista de parámetros

class_name: Nombre de la clase a crear, por defecto utiliza stdClass.
ctor_args: Elementos de este arreglo son pasados al constructor.

Valores retornados

Retorna una instancia de la clase requerida, con el nombre de las propiedades correspondiente a los nombres de las columnas o false in caso de un error.

Ejemplo completo

  1. <?php  
  2.  /* 1. Conexión a la base de datos */  
  3.   
  4.  $dsn = 'mysql:dbname=noticias;host=127.0.0.1';  
  5.  $user = 'dbuser';  
  6.  $password = 'dbpass';  
  7.  try  
  8.     {  
  9.       $dbh = new PDO($dsn, $user, $password);  
  10.     }  
  11.      catch (PDOException $e)  
  12.      {  
  13.        echo 'Connection failed: ' . $e->getMessage();  
  14.      }  
  15.    
  16.  /* Creación la tabla noticias */  
  17.   
  18.  $dbh->query('CREATE TABLE IF NOT EXISTS `noticia` (  
  19.              `id` int(11) NOT NULL AUTO_INCREMENT,   
  20.              `titulo` varchar(50) NOT NULL,   
  21.              `texto` varchar(500) NOT NULL,   
  22.               PRIMARY KEY (`id`)  
  23.             )   
  24.             ENGINE=InnoDB    
  25.             DEFAULT CHARSET=utf8 AUTO_INCREMENT=2');  
  26.   
  27.  /* Insertando datos en la tabla noticias */  
  28.   
  29.  $dbh->query("INSERT INTO `noticia` (`titulo`, `texto`)   
  30.               VALUES ('PHP5 ', 'PHP5 y la POO.')");  
  31.   
  32.  /* 2. Enviar la instrucción SQL a la base de datos */  
  33.   
  34.  $resulset = $dbh->query('SELECT   `titulo`,`texto`   
  35.                           FROM     `noticia`   
  36.                           ORDER BY `titulo`');  
  37.   
  38.  /* 3. Obtener y procesar los resultados */  
  39.   
  40.  $noticia = $resulset->fetchObject();  
  41.  echo $noticia->titulo;  
  42.  echo $noticia->texto;  
  43.  ?>  

Conclusión

PDO cumple con una premisa del diseño Orientado a Objetos:

“No dependas de implementaciones concretas, solo de implementaciones abstractas”[1]

PDO es una implementación abstracta, un paso a mejorar el performance de nuestras aplicaciones con desarrollos más portables y escalables.

Artículos relacionados
  1. PDO (PHP Data Objects). Capa de Abstracción de acceso a Bases de Datos utilizando el Patrón de diseño Singleton (Segunda parte)

Referencias Bibliográficas

[1] Enrique Place, (mayo 25, 2006). «Comentarios sobre: "¿Cual es la mejor capa de abstracción?"». Consultado el 1 de octubre del 2009.

Bibliografía


Colaboradores de Wikipedia. Capa de abstracción [en línea]. Wikipedia, La enciclopedia libre, 2009 [fecha de consulta: 17 de julio del 2009]. Disponible en <http://es.wikipedia.org/w/index.php?title=Capa_de_abstracci%C3%B3n&oldid=28148126>.

Patrón Simple Factory en PHP5

Factory Method o Simple Factory

  
"Clase con la responsabilidad de crear objetos de otras clases. No delega en subclases y sus métodos pueden ser estáticos. Puede evolucionar en un Abstract Factory." [1]


Clasificación del patrón

Creacional.

Intención o problema que soluciona


Centraliza en una clase constructora la creación de objetos de un subtipo de un tipo determinado, ocultando al cliente la casuística para elegir el subtipo que crear.

Motivación

Muchos de ustedes pensaran: ¿Por qué complicarme tanto la vida utilizando un patrón de diseño, en lugar de instanciarla directamente $obj = new MiClase () ?

Hay un escenario principal en el que puede resultar útil.

Flexibilidad en tiempo de ejecución: A veces es imposible elegir de antemano cuál objeto específico debe ser instanciado, ya que la elección de los objetos a utilizar puede depender de algo en el entorno de ejecución.

Aplicabilidad

  1. Una clase no puede prever la clase de objetos que debe crear.
  2. Centralizar la creación de objetos.
  3. Proveer una interfaz para el cliente, permitiendo crear una familia de objetos sin especificar su clase.
Estructura



Implementación

La clase UserFactory tiene un método estático create, el cual recibe como parámetro un argumento y según el valor de este decide que clase instanciar (usualmente una subclase).

Código de Ejemplo en PHP5

Existen diferentes tipos y variantes de fábricas. En este artículo explicaré el Simple Factory.

  1. interface FactoryInterface {  
  2. static public function Create($name);  
  3. }  

  1. abstract class User {  
  2.   
  3.     protected $name = NULL;  
  4.       
  5.     public function __construct($name)  
  6.     {  
  7.         $this->name = $name;  
  8.     }  
  9.   
  10.     public function getName()  
  11.     {  
  12.         return $this->name;  
  13.     }  
  14.   
  15.     public function hasReadPermission()  
  16.     {  
  17.         return true;  
  18.     }  
  19.   
  20.     public function hasModifyPermission()  
  21.     {  
  22.         return false;  
  23.     }  
  24.   
  25.     public function hasDeletePermission()  
  26.     {  
  27.         return false;  
  28.     }  
  29.   
  30.     public function wantsFlashInterface()  
  31.     {  
  32.         return true;  
  33.     }  
  34. }  

  1. class GuestUser extends User { }  

  1. class CustomerUser extends User {  
  2.    function hasModifyPermission()  
  3.     {  
  4.         return true;  
  5.     }  
  6. }  

  1. class AdminUser extends User {  
  2.   
  3.     public function hasModifyPermission()  
  4.     {  
  5.         return true;  
  6.     }  
  7.   
  8.     public function hasDeletePermission()  
  9.     {  
  10.         return true;  
  11.     }  
  12.   
  13.     public function wantsFlashInterface()  
  14.     {  
  15.         return false;  
  16.     }  
  17. }  

  1. class UserFactory  implements FactoryInterface  {  
  2.       
  3.     private static $users = array('Arley'=>'admin', 'Michel'=>'guest',  
  4.                                   'Derick'=>'customer');  
  5.   
  6.    /** 
  7.     * Función de creación de usuarios. Recibe el tipo 
  8.     * de usario a crear y retorna una instancia valida 
  9.     * 
  10.     * @access public static 
  11.     * @param  string $name 
  12.     * @return object 
  13.     */  
  14.     static public function Create($name)  
  15.     {  
  16.         if (!isset(self::$users[$name])) {  
  17.            throw new Exception('El nombre de usuario '.$name.' es desconocido');  
  18.         }  
  19.         switch (self::$users[$name]) {  
  20.             case 'guest': return new GuestUser($name);  
  21.             case 'customer': return new CustomerUser($name);  
  22.             case 'admin': return new AdminUser($name);  
  23.             default:  
  24.             throw new Exception('Tipo de usario desconocido');  
  25.         }  
  26.     }  
  27. }  

  1. function boolToStr($b)  
  2. {  
  3.     if ($b == true) {  
  4.         return "Si\n";  
  5.     } else {  
  6.         return "No\n";  
  7.     }  
  8. }  
  9.   
  10. function displayPermissions(User $obj)  
  11. {  
  12.     print $obj->getName() . ' permiso:</br>';  
  13.     print 'Lectura: ' . boolToStr($obj->hasReadPermission()).'</br>';  
  14.     print 'Modificar: ' . boolToStr($obj->hasModifyPermission()).'</br>';  
  15.     print 'Escritra: ' . boolToStr($obj->hasDeletePermission()).'</br>';  
  16. }  
  17.   
  18. function displayRequirements(User $obj)  
  19. {  
  20.     if ($obj->wantsFlashInterface()) {  
  21.         print $obj->getName() . ' requiere Flash </br></br>';  
  22.     }  
  23. }  
  24.   
  25. $logins = array('Arley', 'Michel', 'Derick');  
  26.   
  27. foreach($logins as $login)  
  28. {  
  29.     displayPermissions(UserFactory::Create($login));  
  30.     print '</br>';  
  31.     displayRequirements(UserFactory::Create($login));  
  32. }  

Otros artículos sobre Patrones
  1. Patron Singleton en PHP
  2. Patrón de diseño Decorator en PHP
  3. Patrón Simple Factory en PHP
  4. Patrón Registry en PHP
  5. Patrón clásico de diseño web Modelo Vista Controlador (MVC) en PHP

Referencias bibliográficas
[1] León Welicki, «Patrones de Fabricación: Fábricas de Objetos». Consultado el 11 de septiembre de 2009.




Patrón de diseño Decorator en PHP5

Patrón de diseño Decorator en PHP5 

Patrón Decorator responde a la necesidad de agregar funcionalidad a un objeto por medio de la asociación de clases. Es un objeto con el que se puede ejecutar funcionalidades de varias clases a la vez.

"Esto nos permite no tener que crear sucesivas clases que hereden de la primera incorporando la nueva funcionalidad, sino otras que la implementan y se asocian a la primera." [1]



Clasificación del patrón

Estructural.

Intención o problema que soluciona


"Supongamos que tenemos una clase existente Ventana y queremos añadirle funcionalidad para que muestre un borde alrededor. Podemos crear una subclase VentanaConBorde que herede de Ventana. Hasta aquí todo bien, pero supongamos que surge la necesidad de crear una ventana que muestre un pequeño botón de ayuda con un signo de interrogación (?) en su parte superior." [1]

Entonces tenemos las siguientes opciones:

1. Crear otra subclase de Ventana: VentanaConBotónDeAyuda.

Problema: No cubre la necesidad de tener ventanas con bordes y botón de ayuda a la vez.

2. Crear una subclase de VentanaConBorde: VentanaConBordeYBotonDeAyuda.

Problema: No tenemos una ventana con botón de ayuda y sin borde.
3. Crear clases para todas las combinaciones posibles de funcionalidades.


Problema: Con este ejemplo tendríamos cuatro clases: Ventana, VentanaConBorde, VentanaConBotonDeAyuda y VentanaConBordeYBotonDeAyuda; con tres funcionalidades tendríamos ocho clases y con cuatro, ¡dieciséis!.

Aplicabilidad
  1. Añadir objetos individuales de forma dinámica y transparente.
  2. Responsabilidades de un objeto pueden ser retiradas.
  3. Cuando la extensión mediante la herencia no es viable.
  4. Hay una necesidad de extender la funcionalidad de una clase, pero no hay razones para extenderlo a través de la herencia.
  5. Hay la necesidad de extender dinámicamente la funcionalidad de un objeto y quizás quitar la funcionalidad extendida. [1]


Código de Ejemplo en PHP5

  1. <?php  
  2. interface iCoffee {  
  3.   public function getBaseCost();  
  4. }  
  5.          
  6. class Coffee implements iCoffee {  
  7.   protected $_baseCost = 0;  
  8.   public function getBaseCost()  
  9.   {  
  10.     return $this->_baseCost;  
  11.   }  
  12. }  
  13.   
  14. class BlackCoffee extends Coffee {  
  15.   public function __construct()  
  16.   {  
  17.     $this->_baseCost = 5;  
  18.   }  
  19. }  
  20.   
  21. abstract class CoffeeDecorator implements iCoffee {  
  22.   protected $_coffee;  
  23.   public function __construct(iCoffee $Coffee)  
  24.   {  
  25.      $this->_coffee = $Coffee;  
  26.   }  
  27. }  
  28.   
  29. class WithCream extends CoffeeDecorator {  
  30.   public function getBaseCost()  
  31.   {  
  32.     return $this->_coffee->getBaseCost() + 1.5;  
  33.   }  
  34. }  
  35.   
  36. class WithMilk extends CoffeeDecorator {  
  37.   public function getBaseCost()  
  38.   {  
  39.      return $this->_coffee->getBaseCost() + 4;  
  40.   }  
  41. }  
  42.   
  43. class WithChocolate extends CoffeeDecorator {  
  44.   public function getBaseCost()  
  45.   {  
  46.     return $this->_coffee->getBaseCost() + 5;  
  47.   }  
  48. }  
  49.   
  50. $coffee = new WithChocolate(new WithMilk(new WithCream(new BlackCoffee())));  
  51. echo 'El precio del cafe es: $' . $coffee->getBaseCost();  

Otros artículos sobre Patrones
  1. Patron Singleton en PHP
  2. Patrón de diseño Decorator en PHP
  3. Patrón Simple Factory en PHP
  4. Patrón Registry en PHP
  5. Patrón clásico de diseño web Modelo Vista Controlador (MVC) en PHP

Referencias bibliográficas

[1] Colaboradores de Wikipedia. Decorator (patrón de diseño) [en línea]. Wikipedia, La enciclopedia libre, 2009 [fecha de consulta: 9 de septiembre del 2009]. Disponible en <http://es.wikipedia.org/w/index.php?title=Decorator_(patr%C3%B3n_de_dise%C3%B1o)&oldid=27813809>.

Patrón Singleton en PHP5

Patrón Singleton en PHP5






El patrón de diseño"Singleton" está diseñado para restringir la creación de objetos pertenecientes a una clase, forzando a que solo se pueda crear una única instancia, de ahí su nombre Singleton (instancia única).






Un ejemplo de uso de este patrón: supongamos que tenemos una clase "Database" y instanciamos dicha clase, la cual nos devuelve un objeto llamado "$conn", una vez creada la conexión con la base de datos esta simple instancia será fácilmente accesible a muchos otros objetos, restringiéndose así a la creación de una única instancia la cual será reutilizada una y otra vez.

¿Pero como funciona el patrón de diseño Singleton?

El patrón Singleton provee una única instancia global debido a que: si se solicita una instancia de la clase:


a) La propia clase es la responsable de crear la única instancia, la cual debe garantizar que no se pueda crear ninguna otra (interceptando todas las peticiones para crear nuevos objetos) y proporcionando un solo punto de acceso a ella.

b) Si no se ha creado anteriormente (o sea, es la primera vez que se usa esta clase) se crea la instancia.

c) Si existe ya anteriormente una instancia (es la segunda o más veces que se usa), se retorna la existente todas las veces que se solicite.

d) El constructor de la clase debe declararse como privado para que la clase no pueda ser instanciada directamente.

e) Adicionalmente en PHP5 no se permite que la clase sea clonada con el método __clone().

De esta forma forzamos a que no se puedan crear objetos de forma directa por la vía del operador "new" ejemplo:

$objeto = new Ejemplo();
//Fatal error:Call to private Ejemplo::__construct() from invalid context

Bueno llegados a este punto se pudieran preguntar

¿Entonces como creo la instancia a la clase si el constructor está declarado como privado?

Directamente a través de un método declarado como público y estático, que será el encargado de crear la instancia de la clase. Ver a continuación:

public static function getInstance() {
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}

Cuando declararamos atributos de clases o métodos como estáticos, los hacemos accesibles desde afuera del contexto del objeto. Utilizamos entonces el operador de resolución :: que permite el acceso a los atributos y métodos estáticos, veamos un ejemplo:

$var = NombreClase::Metodo();

Funcionamiento del método getInstance()

Primeramente accedemos al método:

$inst1 = Ejemplo::getInstance();


Como podemos ver este método verifica el estado del atributo private static $instance;  si está o no definido, para así tomar la decisión de crear la instancia o retornarla directamente en caso de que ya esté creada.

Entonces existen 2 posibles casos:

Caso 1: Si no esta definido el atributo $instance se crea por primera vez la instancia, se almacena en el la referencia y se retorna.


Caso 2: De estar definido el atributo se retorna la instancia almacenada en el directamente.

Implementacion oficial del Patrón Singleton en PHP5:

Esta es la implementación oficial en PHP5 de este patrón les dejo el vinculo http://www.php.net/manual/en/language.oop5.patterns.php , lo único que he cambiado por motivos didácticos y de claridad es el nombre del método singleton() por el de getInstance().

  
class Ejemplo
{
/**
*  Contenedor de la Instancia de la Clase
*
*  @access private static
*/
private static $instance;

/**
*  Constructor
*
*  Previene la creación de objetos via $obj = new Ejemplo();
*
*  @access private
*/
private function __construct() { }

/**
* El método getInstance
*
* Crea una instancia de la clase Ejemplo
*
* @access public static
* @return object de la clase Ejemplo
*/
public static function getInstance()
{

if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;

}

return self::$instance;
}
/**
* El método __clone()
*
* Previene que la clase sea clonada
*
* @access public
*/
public function __clone()
{
trigger_error('Clone no se permite.', E_USER_ERROR);

}
}

/**
* Ejemplo de uso
*
* Accedo directamente al método getInstance()
* para que me devuelva una instancia de la clase Ejemplo,
* como dicha instancia no existe este método, la crea.
*
* Nota: si hacemos var_dump sobre un variable esta función
* mostrará información estructurada que incluye tipos y valores.
* En el caso de las variables que contienen objetos esta funcion
* devolvería tipo de la variable, en este caso
* object, entre paréntesis (nombre de la clase) y
* un número único #id que representa al objeto creado.
* Podemos ver que dicho número #id es el mismo para estas
* dos primeros casos.
*/
$inst1 = Ejemplo::getInstance();


var_dump($inst1);
/** * En el segundo caso, como existe ya la instancia, * el método getInstance() lo comprueba y no la crea, * sino que la retorna directamente. */ $inst2 = Ejemplo::getInstance();
var_dump($inst2);
/** * Si intento crear una instancia directamente utilizando * el operador new. * * Fatal error: Call to private Ejemplo::__construct() * from invalid context */ $inst3 = new Ejemplo();
¿Ventajas y usos?

Suponiendo que utilizamos el Singleton en una Base de Datos, esto posibilita que:

1.) Que nuestra aplicación web ahorre recursos del servidor.
2.) Tenemos bien controlado la creación de objetos, al no permitir que nuestra aplicación tenga innumerable y descontroladas conexiones innecesarias abiertas a una misma base de datos.
Bibliografía
  1. Enrique Place, (7 de noviembre del 2006). «Patrón de Diseño "Singleton"». Consultado el 25 de agosto de 2009.
  2. Colaboradores de Wikipedia. Patrón de diseño [en línea]. Wikipedia, La enciclopedia libre, 2009 [fecha de consulta: 17 de septiembre del 2009]. Disponible en <http://es.wikipedia.org/w/index.php?title=Patr%C3%B3n_de_dise%C3%B1o&oldid=29814016>. 
  3. Colaboradores de Wikipedia. Singleton [en línea]. Wikipedia, La enciclopedia libre, 2009 [fecha de consulta: 1 de septiembre del 2009]. Disponible en <http://es.wikipedia.org/w/index.php?title=Singleton&oldid=29359982>.