15 de abril de 2015

Patrón Registry

La idea de este patrón es crear una clase que tenga un array donde guardar los objetos registrados y una serie de métodos: uno que añada los objetos al array, otro que compruebe su existencia para evitar duplicaciones, otro que devuelva el objeto pasándole su clave, uno que borre el objeto recibiendo la clave y finalmente uno que limpie el array de la clase.
<?php
class registry{
     
     static private $registry = array();
 
     public function add($name, $item)
     {
         if (!self::exists($name)) {
             self::$registry[$name] = $item;
             return true;
         } else {
             return false;
         }
     }
 
     public function exists($name)
     {
         if (is_string($name)) {
             return array_key_exists($name, self::$registry);
         } else {
             throw new Exception('Registry item\'s name must be a string');
         }
     }
 
     public function get($name)
     {
         if (self::exists($name)) {
             $return = self::$registry[$name];
         } else {
             $return = null;
         }
         return $return;
     }
 
     public function remove($name)
     {
         if (self::exists($name)) {
             unset(self::$registry[$name]);
         }
         return true;
     }
 
     public function clear()
     {
         self::$registry = array();
     }
 }
?>
Si nos fijamos es una clase estática por lo que podemos acceder a sus métodos sin necesidad de instanciar la clase, dentro de sus métodos los principales son el método add() y el método get(), mediante estos métodos vamos a llenar la instancia y a mostrar el resultado de la instancia, pero como lo hacemos?? Para crear un valor en el patrón registry usamos el método add este nos pide dos parámetros el primero es el nombre que va a llevar el valor y el segundo es el valor como tal de este veamoslo con un ejemplo:
registry::add('nombre','aca esta el valor que deseamos mostrar');
Sencillamente asignamos a nombre en esta instancia el valor: aca esta el valor que deseamos mostrar, sencillo no y como utilizamos o se muestra el valor que acabamos de asignar?? Utilizando el método get(), de esta manera:
echo registry::get('nombre');
El resultado nos mostraría por pantalla “aca esta el valor que deseamos mostrar”, fácil verdad?? Los demás métodos nos sirven para validación de que exista un índice, limpiar e incluso un quitar índice agregado mediante una llamada a este patrón, fácil verdad?? Que nos permite hacer el Patrón Registry?? Sencillamente nos permite mantener y modificar valores e instancias entre clases diferentes e incluso fuera de las mismas (es un concepto personal si alguien tiene objeción la puede realizar y así aprendemos más), a medida que veamos el código se entenderá mas este pequeño concepto, un pequeño ejemplo:
<?php
a.php
class a
{
  public function llenar($valor)
 {
  registry::add('indice',$valor);
 }
}
?>
espacio
<?php
b.php

class b
{  
 public function mostrar()
 {
  echo registry::get('indice');
 }
}
?>
espacio
<?php
Index.php
require_once("registry.php");
require_once("a.php");
require_once("b.php");

 $a = new a();
 $b = new b();
 $a->llenar("hola");
 $b->mostrar(); 
?>
Si se fijan en la primera clase asignamos a nuestro patrón registry un valor, en la segunda clase sencillamente mostramos el valor del índice que llenamos en la primera clase sencillo su uso no?? Veamos ahora los demás métodos en acción
<?php
b.php
class b
 {  
 public function mostrar($indice)
 {
  if(registry::exists($indice)){
   echo registry::get('indice');
  } 
  else{
   echo "El valor buscado no existe";
  }
 }
 }
?>
re-declaramos la clase b para utilizar más métodos de nuestro patrón registry
<?php
index.php
$a = new a();
 $b = new b();
 $a->leer("hola");
 $b->mostrar('indice');
 registry::remove('indice');
 $b->mostrar('indice');
?>

Patrón Factory PHP

El patrón Factory permita la instancia de objetos en tiempo de ejecución, la ventaja de utilizar este patrón de diseño es que tenemos centralizada la creación de los objetos en una clase que a pedido, nos devuelve instancias de los mismos, permitiendo tener el control de la creación de objetos.

Este modelo nos aporta una mayor claridad a la hora de programar, puede que para pequeños proyectos no sea muy útil pero en el caso de los grandes o donde trabajen varias personas puede ser muy socorrido.

Como ventajas podemos citar la centralización de creación de objetos, todo lo haríamos a través de una sola clase por lo que podríamos definir los mismos métodos y acciones para todos los objetos de una aplicación.

Primera aproximación: Familia de Clases Independientes

Se busca crear un nuevo objeto de una clase, en este ejemplo al instanciar una clase se podrán utilizar los métodos y variables que esta contenga (siempre y cuando sean public o protected), lo interesante aquí es que cada clase es independiente de otra, sin importar que pertenezcan a una misma familia de clases, en otras palabras: si bien tienen aspectos en común, no tienen un padre que les oriente en su comportamiento sino que cada clase define su comportamiento.

La ventaja de esta primera aproximación es que se conoce de los atributos y comportamiento de las clases que se instancian al estudiar cada clase independiente pues están tienen detallado en su código la función que realizan y no se debe buscar una clase padre cuyos métodos puedan estar inaccesibles debido a niveles de protección de documentos. Hago la diferencia pues en este ejemplo se utilizaran dos clases que tienen en común que realizan y entregan una conexión a una base de datos específica, sin embargo no presentan herencia de una clase abstracta que les indique los parámetros estáticos o las funciones establecidas que pueden utilizar o bien las funciones abstractas que deben definir, Los pasos para utilizar el patrón Factory son:

1. Crear o especificar en una familia las clases que se desean instanciar desde un punto único, por lo general son clases que tengan algo en común a fin de lograr una mejor optimización.

2. Importar la(s) case(s) hacia una “Clase Factory” que será la encargada de crear las instancias de las clases importadas a través de un método estático que se puede instanciar desde fuera del objeto. Este método tiene que recibir el nombre de la clase a instanciar.

3. Comprobamos que la clase solicitada exista(if(class_exists($type))) en caso de existir se devuelve una instancia de dicha clase Ejemplo:

<?php
// Clase para la utilización de base de datos MySQL

class MySQL{
 public function __construct()
 {
  echo "
Constructor MySQL";
 }

// Funcion para conectar con la base de datos...
 public function connect()
 {
  //$link=mysql_connect(...);
  echo "
Conectar con MySQL";
 }
}

// Clase para la utilización de base de datos PostgreSQL
class PostgreSQL{
 public function __construct()
 {
  echo "
Constructor PostgreSQL";
 }

 // Funcion para conectar con la base de datos
 public function connect()
 {
  //link=pg_connect(...);
  echo "
Conectar con PostgreSQL";
 }
}

class database{

 //Funcion "static" que se puede instanciar desde fuera del objeto.
 // Tiene que recibir el nombre de la clase a instanciar
 
  public static function factory($type){
  if(class_exists($type))
  {
   //Devolvemos una instancia de la clase
   return new $type();
  }else
   echo "
Error. la case '".$type."' no existe";
  return 0;
 }
}

Require_once(“database.php”)
Class prueba{

//Realizamos una instancia para el tipo de base de datos MySQL
$obj1=database::factory("MySQL");
$obj1->connect();

//Realizamos una instancia para el tipo de base de datos PostgreSQL
$obj2=database::factory("PostgreSQL");
$obj2->connect();
}
?>

Segunda aproximación: Familia de Clases Dependientes

En esta aproximación se busca crear un nuevo objeto de una sub clase, por lo que en este ejemplo se define una clase abstracta que hace el papel de padre orientando el comportamiento de las clases hijas, la desventaja de esta segunda aproximación es que puede darse el caso en que se instancien clases hijas y no se tenga conocimiento de los métodos definidos de la clase padre.

Cabe resaltar lo anterior pues en ciertos escenarios o ciertos “programadores” toman a la clase padre (abstracta o no) que define el comportamiento de las clases hijas como el pensamiento supremo y mantienen esta clase en una carpeta con niveles de protección a fin que no sea accesible para otros programadores…claro que su razón tienen y “el gran inconveniente” que te expongo se soluciona con una buena documentación.

Los pasos para utilizar el patrón Factory son:
1. Crear una clase abstracta la cual por definición nunca será instanciada, esta clase estará fuera del alcance del usuario y siendo con métodos definidos o abstractas definirán el comportamiento de las clases hijas que la hereden.

2. Implícito: Los métodos de la clase abstracta que serán utilizados por las clases hijas deben ser declarados como public fin de que sean accesibles primero por las clases hijas y luego por la clase que haga uso de la “Clase Factory”.

3. Crear o especificar en una familia las clases que se desean instanciar desde un punto único, por lo general son clases que tengan algo en común a fin de lograr una mejor optimización.

4. Importar la(s) clase(s) hacia una “Clase Factory” que será la encargada de crear las instancias de las clases importadas a través de un método estático que se puede instanciar desde fuera del objeto…tener en cuenta el magic method __autoload(). Este método tiene que recibir el nombre de la clase a instanciar

5. Comprobamos que la clase solicitada exista(if(class_exists($type))) en caso de existir se devuelve una instancia de dicha clase
<?php 

// Solamente apuntar que las clases pueden ubicarse
// en distintos directorios

abstract class HtmlComponent{
        protected $_html;
        public function getHtml(){
                return $this->_html;
        }
}

class ImageHtmlComponent extends HtmlComponent{
        public function __construct(){
  $this->_html = 'Factory Design';
  }
}
  
class InputHtmlComponent extends HtmlComponent{
  public function __construct(){       
    $this->_html = '';
       }
}

// Llamamos a las clases hijas con un require o 
// bien con __autoload()
// nunca invocar la clase abstracta

class HtmlComponentFactory{
        public static function createHtmlComponent($type){
                $baseclass = 'HtmlComponent';
                $targetclass = ucfirst($type).$baseclass;
                if(class_exists($targetclass) && is_subclass_of($targetclass, $baseclass)){
                        return new $targetclass;
                }
                else{
                        die("El tipo '$type' no existe");                 
                }
        }
}

$components = array('Image', 'Input');
foreach($components as $component){
        echo HtmlComponentFactory::createHtmlComponent($component)->getHtml();
}
?>

Tercera aproximación: Clase Factory Abstracta

Esta aproximación es una extensión de la primera aproximación: Familia de clases independientes, aquí las clases están debidamente definidas y tiene un comportamiento común lo que permite agruparlas en una familia, pero es una clase abstracta a través de un método estático quien se encargara de la creación de las instancias.

La característica de esta aproximación es que la clase que quiera utilizar la “Clase Factory” deberá heredar esta clase haciendo uso de extends, además recordemos que una clase abstracta define modelos abstractos que deberán ser implementados por aquellas clases que la hereden, por lo que debe utilizarse esta aproximación para extender el comportamiento de las clases definidas independientemente.

Basándonos en Wikipedia: Factory Method consiste en utilizar una clase constructora (al estilo del Abstract Factory) abstracta con unos cuantos métodos definidos y otro(s) abstracto(s): el dedicado a la construcción de objetos de un subtipo de un tipo determinado. Es una simplificación del Abstract Factory, en la que la clase abstracta tiene métodos concretos que usan algunos de los abstractos; según usemos una u otra hija de esta clase abstracta, tendremos uno u otro comportamiento.

He aquí un ejemplo con varias clases para conectarse a diferentes base de datos, sin importar donde desee conectarme concentraré la instancia en una clase llamada BaseDeDatos

<?php
 
class MySQL{
  public function __construct(){
    echo "Instancio MySQL";
  }
}
 
class PostgreSQL{
  public function __construct(){
    echo "Instancio PostgreSQL";
  }
}
 
// Clase abstracta con método estatico publico encargado
// de crear y entregar las instancias que fabrica

abstract class BaseDeDatos{
    public static function cargar($tipo){
        try {
            if (class_exists($tipo)) {
                return new $tipo;
            } else {
                throw new Exception("No existe la clase '$tipo'");
            }
        } catch (Exception $e) {
            echo 'Excepción capturada: ',  $e->getMessage(), "\n";
        }
    }
}
 
BaseDeDatos::cargar("MySQL");
echo "
";
BaseDeDatos::cargar("PostgreSQL");
echo "
";
BaseDeDatos::cargar("Oracle");
?>

Función útil: __autoload() __autoload funciona de la siguiente manera: Cuando se necesita una clase (haciendo un new, en un extends, un implements, etc…) si la clase no está cargada se llama a la función mágica __autoload con el nombre de la clase como parámetro, puede serte útil al implementar el patrón Factory.

<?php

function __autoload($fileName){
    $file = 'clases/' . $fileName.'.php';
    if (file_exists($file)){
        require_once $file;
    }
}
 
$nuevo = new nuevo;
?>

14 de abril de 2015

Apuntes PDO PHP

PDO proporciona una capa de abstracción de acceso a datos, lo que significa que, independientemente de la base de datos que se esté utilizando, se usan las mismas funciones para realizar consultas y obtener datos. Para cada base de datos existe un manejador (driver) específico, que debe estar habilitado en el archivo de configuración de PHP (el archivo php.ini). Los manejadores se administran mediante extensiones de PHP, las cuales tienen nombres finalizando con dll en Windows y con so en Unix.
extension=php_pdo.dll
extension=php_pdo_firebird.dll
extension=php_pdo_informix.dll
extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
extension=php_pdo_oci.dll
extension=php_pdo_oci8.dll
extension=php_pdo_odbc.dll
extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll
Ejemplo conexiones PDO
<?php
try {
 // Conexión a MS SQL Server 
  $cnn = new PDO("mssql:host=$host;dbname=$dbname, $user, $pass");
  
  // Conexión a Sybase
  $cnn = new PDO("sybase:host=$host;dbname=$dbname, $user, $pass");
 
  // Conexion a MySQL 
  $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
 
  // Conexion a SQLite
  $DBH = new PDO("sqlite:my/database/path/database.db");
}
catch(PDOException $e) {
    echo $e->getMessage();
}
?>
Conexion PDO-MySQL
<?php
try{
        $this->db = new PDO(DB_MOTOR.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
        $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        $this->db->setAttribute(PDO::ATTR_PERSISTENT,true);
        $this->db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
   
   }catch(PDOException $e){
         echo 'Ha surgido un error y no se puede conectar a la base de datos' .E_USER_ERROR. PHP_EOL;
         echo "";
         echo "Detalle: ".$e->getMessage();
         exit;
      }
?>
Ejemplo Excepciones PDO
$cnn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$cnn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$cnn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
es mas util la opcion ERRMODE_EXCEPTION, aqui el ejemplo
try {
  $cnn = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
  $cnn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
 
  $cnn->prepare('DELECT name FROM people');
}
catch(PDOException $e) {
    echo "Error en la consulta";
    file_put_contents('PDOErrors.txt', $e->getMessage(), FILE_APPEND);
}
Ejemplo de sentencia SELECT
$sql= "SELECT filmID, filmName, filmDescription, filmImage, filmPrice, filmReview FROM movies WHERE filmID = :filmID"; 
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':filmID', $filmID, PDO::PARAM_INT); 
$stmt->execute();
$obj = $stmt->fetchObject();
echo $obj->filmName;
Ejemplo de Unnamed Placeholders
Es necesario mantener el orden al momento de especificar los valores de la sentencia
<?php
 ini_set('display_errors', '0');
 include_once('connect.php');
 include_once('error_handling.php');

 $team = 'Yamaha';
 $status = 'Factory';

 $sqlQuery = "SELECT * from riders where team = ? AND status = ?";

 $statement = $dbh->prepare($sqlQuery);

 $statement->bindValue('1', $team, PDO::PARAM_STR); 
 $statement->bindValue('2', $status, PDO::PARAM_STR); 

 try{
  $statement->execute();
 }
 catch(PDOException $error){
  echo errorHandling($error);
 }
 while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
  echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!
";
 }
?>
Ejemplo de Named Placeholders
No es necesario estar pendiente del orden de los parámetros pero si de especificar adecuadamente el par clave=>valor.
<?php
 ini_set('display_errors', '0');
 include_once('connect.php');
 include_once('error_handling.php');

 $team = 'Yamaha';
 $status = 'Factory';

 $sqlQuery = "SELECT * from riders where team = :team AND status = :status";

 $statement = $dbh->prepare($sqlQuery);

 $statement->bindParam(':team', $team, PDO::PARAM_STR);
 $statement->bindParam(':status', $status, PDO::PARAM_STR);

 try{
  $statement->execute();
 }
 catch(PDOException $error){
  echo errorHandling($error);
 }
 while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
  echo $row['name'] . " rides a " . $row['team'] . " " . $row['status'] . " MotoGP bike!
";
 }
?>
Ejemplo Sentencia INSERT
<?php
$nom = 'Jose';
$ape = 'Hernandez';
 
try
{
  $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
  $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 
  $stmt = $con->prepare('INSERT INTO personal (nombre, apellidos) VALUES (:nombre, :apellidos)');
  $rows = $stmt->execute( array( ':nombre'   => $nom,
                                    ':apellidos' => $ape));
 
  if( $rows == 1 )
    echo 'Inserción correcta';
}
catch(PDOException $e)
{
  echo 'Error: ' . $e->getMessage();
}
?>
Ejemplo Sentencia UPDATE
<?php
$nom = 'Jose';
$ape = 'Hernandez';
 
try
{
  $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
  $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 
  $stmt = $con->prepare('UPDATE personal SET apellidos = :apellidos WHERE nombre = :nombre');
  $rows = $stmt->execute( array( ':nombre'   => $nom,
                                    ':apellidos' => $ape));
  if( $rows > 0 )
    echo 'Actualización correcta';
}
catch(PDOException $e)
{
  echo 'Error: ' . $e->getMessage();
}
?>
Ejemplo Sentencia DELETE
<?php
$ape = 'Hernandez';
 
try
{
  $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
  $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 
  $stmt = $con->prepare('DELETE FROM personal WHERE apellidos = :apellidos');
  $rows = $stmt->execute( array( ':apellidos' => $ape));
 
  if( $rows > 0 )
    echo 'Borrado correcto';
}
catch(PDOException $e)
{
  echo 'Error: ' . $e->getMessage();
}
?>
Mapeo de Objetos
PDO nos permite realizar consultas y mapear los resultados en objetos de nuestro modelo. Para ello primero tenemos que crearnos una clase con nuestro modelo de datos.
<?php
// la clase puede estar en una
// ubicación o folder especifico 

class Usuario{
  private $nombre;
  private $apellidos;
 
  public function nombreApellidos()
  {
    return $this->nombre . ' ' . $this->apellidos;
  }
}
?>
como ejemplo se utilizara una sentencia SELECT, Hay que tener en cuenta que para que funcione correctamente, el nombre de los atributos en nuestra clase tienen que ser iguales que los que tienen las columnas en nuestra tabla de la base de datos. Con esto claro vamos a realizar la consulta.
<?php
try
{
  $con = new PDO('mysql:host=localhost;dbname=personal', 'user', 'pass');
  $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 
 $stmt= $con->prepare('SELECT nombre, apellidos FROM personal');
  $stmt->execute();
 
  $stmt->setFetchMode(PDO::FETCH_CLASS, 'Usuario');
 
  while($usuario = $stmt->fetch())
    echo $usuario->nombreApellidos() . '
';
 
}
catch(PDOException $e)
{
  echo 'Error: ' . $e->getMessage();
}
?>
Ejemplo INSERT con Object Mapping
<?php
class person {
    public $name;
    public $addr;
    public $city;
 
    function __construct($n,$a,$c) {
        $this->name = $n;
        $this->addr = $a;
        $this->city = $c;
    }
    # etc ...
}
 
$cathy = new person('Cathy','9 Dark and Twisty','Cardiff');
 
$STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)");
$STH->execute((array)$cathy);
?>
FETCHMODE en consultas SELECT Utilizando query() y PDO::FETCH_NUM
<?php
$sql = 'SELECT name, colour, calories FROM fruit';
try {
  $stmt = $dbh->query($sql);
  $result = $stmt->setFetchMode(PDO::FETCH_NUM);
  while ($row = $stmt->fetch()) {
    print $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n";
  }
}
catch (PDOException $e) {
  print $e->getMessage();
}
?>
Utilizando query() y PDO::FETCH_ASSOC
<?php
$stmt = $dbh->prepare("SELECT mykey, myvalue FROM mytable");
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$arrValues = $stmt->fetchAll();
foreach ($arrValues as $row){
    print $row["mykey"]." -> ".$row["myvalue"]."
\n";
}
?>
Utilizando query() y PDO::FETCH_OBJ
<?php
$STH = $DBH->query('SELECT name, addr, city from folks'); 

$STH->setFetchMode(PDO::FETCH_OBJ);
 
while($row = $STH->fetch()) {
    echo $row->name . "\n";
    echo $row->addr . "\n";
    echo $row->city . "\n";
}
?>
Utilizando Unnamed Placeholders, execute() y PDO::FETCH_ASSOC
<?php
try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
 
    $sql = 'SELECT lastname, firstname, jobtitle 
            FROM employees
            WHERE lastname LIKE ?';
 
    $q = $conn->prepare($sql);
    $q->execute(array('%son'));
    $q->setFetchMode(PDO::FETCH_ASSOC);
 
    while ($r = $q->fetch()) {
        echo sprintf('%s 
', $r['lastname']);
    }
} catch (PDOException $pe) {
    die("Could not connect to the database $dbname :" . $pe->getMessage());
}
?>
Utilizando Named Placeholders, execute() y PDO::FETCH_ASSOC
<?php
try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
 
    $sql = 'SELECT lastname, firstname, jobtitle 
            FROM employees
            WHERE lastname  LIKE :lname OR
                  firstname LIKE :fname';
 
    $q = $conn->prepare($sql);
    $q->execute(array(':fname' => 'Le%',
                      ':lname' => '%son'));
    $q->setFetchMode(PDO::FETCH_ASSOC);
 
    while ($r = $q->fetch()) {
        echo sprintf('%s 
', $r['lastname']);
    }
} catch (PDOException $pe) {
    die("Could not connect to the database $dbname :" . $pe->getMessage());
}
?>
Transacciones en PDO
Debido a que no todas las bases de datos soportan transacciones, PHP corre en el modo de auto-commit que ejecuta cada instrucción individual en forma implícita. Si se desea usar transacciones, y no se desea utilizar el modo de auto-commit, es necesario invocar el método PDO::beginTransaction()al inicio de la transacción. Si el manejador de la base de datos no permite el uso de transacciones se producirá una excepción (PDOException). Cuando se acabe de especificar la transacción se pueden utilizar los métodos PDO::Commit para aplicar dichas instrucciones, o bien, PDO::rollBack para abortar dicha transacción.

<?php
try {
  $stmt = $dbh->prepare("INSERT INTO countries (name, area, population, density)
                                VALUES (:name, :area, :population, :density)");
  $stmt->bindParam(':name', $name);
  $stmt->bindParam(':area', $area);
  $stmt->bindParam(':population', $population);
  $stmt->bindParam(':density', $density);

  $dbh->beginTransaction();
  $name = 'Nicaragua'; $area = 129494; $population = 602800; $density = 46.55;
  $stmt->execute();
  $name = 'Panama'; $area = 78200; $population = 3652000; $density = 46.70;
  $stmt->execute();
  $dbh->commit();

} catch (Exception $e) {
  $dbh->rollBack();
  echo "Failed: " . $e->getMessage();
}
?>
o bien este ejemplo en que se utiliza un array para especificar los valores de la consulta
<?php
try {
  $stmt = $dbh->prepare("INSERT INTO countries (name, area, population, density)
                                VALUES (?, ?, ?, ?)");

  $dbh->beginTransaction();
  $stmt->execute(array('Nicaragua', 129494, 602800, 46.55));
  $stmt->execute(array('Panama', 78200, 3652000, 46.70));
  $dbh->commit();

} catch (Exception $e) {
  $dbh->rollBack();
  echo "Failed: " . $e->getMessage();
}
?>

Conexiones Persistentes PHP

La idea detrás de las conexiones persistentes es que una conexión entre un proceso cliente y una base de datos puede ser reutilizados por un proceso cliente, en lugar de ser creado y destruido varias veces. Esto reduce la sobrecarga de la creación de conexiones nuevas cada vez que es requerido, como las conexiones no utilizadas se almacenan en caché y están lista para ser reutilizadas.

este ejemplo PDO-MySQL utiliza conexiones persistentes
<?php
try{
        $this->db = new PDO(DB_MOTOR.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
        $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        $this->db->setAttribute(PDO::ATTR_PERSISTENT,true);
        $this->db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
   
   }catch(PDOException $e){
         echo 'Ha surgido un error y no se puede conectar a la base de datos' .E_USER_ERROR. PHP_EOL;
         echo "";
         echo "Detalle: ".$e->getMessage();
         exit;
      }
?>
Cuando un script php necesita conectarse a un SGBDR, la función PDO con su atributo PDO::ATTR_PERSISTENT primero intenta encontrar un enlace (persistente) que ya esté abierto con el mismo anfitrión, nombre de usuario y contraseña. Si se encuentra uno, se devolverá un identificador para él, en lugar de abrir una nueva conexión.

Consideraciones sobre conexiones persistentes
Al finalizar, la conexión al SGBDR no será cerrada cuando la ejecución del script finalice. En su lugar, el enlace permanecerá abierto para su uso futuro.

Tener en cuenta que PHP no tiene una funcion para "desconectar" las conexiones persistentes.

En las conexiones persistentes se pueden encontrar estados impredecibles por los clientes. Por ejemplo, un bloqueo de tabla puede ser activado antes de que un cliente termina de forma inesperada. Un nuevo proceso cliente reusando esta conexión persistente tendrá la conexión "tal cual". Toda la limpieza tendría que ser realizada por el nuevo proceso cliente antes de que pudiera hacer buen uso de la conexión persistente, aumentando la carga sobre el programador.

Las conexiones persistentes son eficientes si los gastos indirectos para crear un acoplamiento a tu servidor del SQL son altos, por ejemplo porque el web server no funciona en la misma computadora que el servidor de la base de datos:
Si el servidor web con sus páginas php y los servidores de bases de dados están en el mismo ordenador, no puedes notar diferencia de velocidad entre conexiones persistentes y no persistentes. Si no están en el mismo ordenador, pero sí están en una misma subred privada rápida, seguramente tampoco notarás la diferencia de velocidad. Las conexiones persistentes tienen sentido cuando el servidor de bases de datos está en un ordenador remoto, con una conexión lenta, a través de internet por ejemplo.

Las conexiones persistentes no te dan ninguna funcionalidad que no sea posible con las conexiones no-persistentes.

Debes siempre poder substituir conexiones persistentes con las conexiones no-persistentes.

El uso de conexiones persistentes puede requerir ajustar un poco las configuraciones de Apache y de MySQL para asegurarse de que no se excede el número de conexiones permitidas por MySQL.

Por lo general se maneja un archivo de configuracion con los parametros para conectarse a la base de datos, pero en caso de estar en un ambiente de desarrollo ten en cuenta que si tienes un servidor llamado por ejemplo miservidor.com, con ip pública 123.123.123.123, e ip local 192.168.0.1 y unas veces abres conexión contra el nombre de servidor, otras veces contra la ip pública, otras contra la local y otras veces contra localhost, son conexiones distintas cada vez, y vas a multiplicar por 4 el número de conexiones persistentes.
Y más grave aún, esto es muy típico, si tienes en tu servidor varias webs distintas que se corresponden con distintas bases de datos de cada cliente, es normal que configures el servidor de bases de datos con un nombre de usuario y password distinto para cada cliente (con la orden GRANT), y que desde cada programa php conectes con un nombre de usuario y password
distinto según la base de datos a usar, pues en este escenario vas a abrir tantas conexiones persistentes como número de usuarios y passwords distintos tengas, pues para que una conexión persistente se reutilice, TODOS los parámetros de conexión han de ser idénticos.

Las conexiones persistentes no solo dependen de php y la base de datos, tambien es importante configurar estos parametros en httpd.conf

# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On

# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests [valor numerico]

# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout [valor numerico]

En el archivo de configuracin php.ini tambin se puede definir cuantas conexiones de puede abrir tanto persistentes como no persistentes

13 de abril de 2015

Patrón Singleton PHP

Singleton o instancia única es un patrón de diseño cuya función es crear un único acceso global para restringir la creación de objetos pertenecientes a una clase o el valor de un tipo a un único objeto, de forma que garantiza que la clase sólo tenga una instancia además de proporcionar un acceso al objeto en toda la aplicación, de ahí su nombre Singleton (instancia única).

A continuación el ejemplo, tengo creada una base de datos llamada ejemplo y una tabla creada en esta base de datos de nombre empleados, lo primero sera crear un archivo php llamado parametros.php donde se especificaran los datos de conexion, este archivo lo guardare en una carpeta llamada CFG

<?php

// CFG/parametros.php guarda los parametros de conexion a la base de datos

    define('DB_MOTOR','mysql'); 
    define('DB_HOST','localhost'); 
    define('DB_USER','root'); 
    define('DB_PASS',''); 
    define('DB_NAME','ejemplo'); 
    define('DB_CHARSET','utf-8'); 

?>
Esta es la clase cnn.class.php que implementa el patrón de diseño Singleton
<?php
//cnn.class.php se encarga de crear la instancia unica del objeto

require_once "CFG/parametros.php";
class Conexion {

private static $_instancia;
private $_db;

 public static function getInstance() {
  if(!self::$_instancia) {
   self::$_instancia = new self();
                      /* otra opcion
                        $c = __CLASS__;
             self::$instancia = new $c;
        */
                 }
  return self::$_instancia;
 }


   private function __construct() {
      try{
        $this->db = new PDO(DB_MOTOR.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
        $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        $this->db->setAttribute(PDO::ATTR_PERSISTENT,true);
        $this->db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
  
   }catch(PDOException $e){
         echo 'Ha surgido un error y no se puede conectar a la base de datos' .E_USER_ERROR. PHP_EOL;
         echo "";
         echo "Detalle: ".$e->getMessage();
         exit;
      }
   }
  
   public function getConnection() {
  return $this->db;
 }
   
    public function CloseConnection(){
        return $this->db=null;
    }
   
    public function __clone(){
         trigger_error('No esta permitido clonar esta clase', E_USER_ERROR);
    } 

    public function __wakeup(){
         trigger_error("No puede deserializar una instancia de ". get_class($this) ." class.", E_USER_ERROR );
    }
}
?>

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().

Para hacer uso de la clase que implementa elpatron Singleton se crea el script test.php el cual solamente prueba la conexión a la base de datos y luego se desconecta

<?php
// test.php utiliza la instancia unica
// que se crea en la clase cnn.class.php
// utilizando el patron Singleton

require_once 'cnn.class.php';
$db1 = Conexion::getInstance();
$db = $db1->getConnection();
if(!is_null($db)){
   $stmt = $db->query('SELECT * FROM empleados');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
}
else {
    echo"No se realizo la conexion";
}

try{
    $db = $db1->CloseConnection();
    echo "conexion cerrada";
}
catch(Exception $e){
    echo $e.getmessage();
}
?>

Apuntes del Patrón Singleton

Muchos caen en el cuento de Singleton de crear una instancia única para toda la aplicación pero en ambientes web se debe tener en cuenta la palabrita "stateless", básicamente indica que cada petición al servidor es tratada independientemente, es decir, si entra Jose a tu sitio web y hace operaciones sobre la base de datos y al mismo tiempo esta Pedro realizando operaciones sobre la base de datos(aunque no necesariamente sobre la misma tabla)ambas peticiones, ambos usuarios son tratados independientemente, basado en lo anterior, Singleton me garantiza una instacia única para cada usuario, no para toda la aplicación.

Se puede implementar perfectamente el patrón Singleton con PHP, pero por el contexto web, este se aplicará normalmente durante la vida de una única página, a menos que implementemos algún mecanismo para persistir el objeto Singleton y así lograr que este perdure durante toda la vida de la aplicación web
Aun cuando el patrón Singleton no cumple su promesa en ambientes web, tiene sus defensores en ambientes cliente servidor, aunque bien utilizar Dependecy Injections seria un a mejor alternativa de desarrollo igualmente una abstracción de la base de datos presenta una solución mas escalable.

Hay comentarios en que tratan a Singleton como anti-patrón y que mas les vale utilizar variables globales, la verdad si un desarrollador argumenta que utilizar variables globales es LA SOLUCIÓN pues no hay mas que hablar.

Aunque posible, realizar pruebas unitarias es un dolor de cabeza, igualmente algunos se quejan de que el patrón Singleton oculta la logica de la clase que instancia y que al momento de desarrollar modulos que dependan de la clase Singleton se les hará un dolor de cabeza el tener un constructor privado o el no conocer la lógica de la clase, pero eso depende de tu entorno, de que tanta confianza te tenga tu equipo de desarrollo, en mi opinión, aunque vale la pena el mencionarlo no lo veo como un problema.

Las transacciones funcionan correctamente en Singleton pues cada transacción es tratada por separado, así pues, Jose hará sus commit o rollback a la base de datos independientemente de los commit y rollback de Pedro.

La instancia del singleton la creas tú, y por tanto controlas su ciclo de vida igual que cualquier otro objeto.

Puedes crear una clase que herede de la clase que tienes definida como singleton, no tiene nada raro. Sin embargo, dado que no puedes sobreescribir los métodos estáticos, no tiene sentido heredar de la clase de métodos estáticos.


syntax





7 de abril de 2015

Autocarga de clases __autoload() PHP

Cuando se escriben aplicaciones orientadas a objetos en PHP los archivos con las clases generalmente van dentro de una carpeta especifica, de esto saca ventaja la función __autoload para crear instancias de esas clases, __autoload no es una función definida en PHP, __autoload es una función que debemos definir y ésta será ejecutada cuando se instancie una clase. Entonces si nosotros almacenamos todas nuestras clases dentro de una carpeta clases podremos instanciar la clase sin necesidad de incluir el archivo que contenga la definición de la clase. Obviamente si el archivo no está incluido producirá un error. Un ejemplo muy común es definir el autoload y que este incluya el archivo fuente donde este la definición de la clase.
<?php
//index.php
function __autoload($fileName){
    $file = 'clases/' . $fileName.'.php';
    if (file_exists($file)){
        require_once $file;
    }
else {
        throw new Exception("Unable to load $class_name.");
    }
}

try {
$nuevo = new nuevo;
} catch (Exception $e) {
    echo $e->getMessage(), "\n";
} 
?>
La clase esta definida en la ruta clases/nuevo.php la cual se especifica en la función __autoload()
<?php
class nuevo{
    function __construct(){
        echo "Clase 'nuevo' ha sido instanciada";
    }
}
?>
En este ejemplo es importante que tanto el nombre del archivo como el nombre de la definición de la clase sean los mismos para que no produzcan un error. Cuando hago un new nuevo;, antes de hacer efectiva la instancia se ejecuta la función __autoload (debido a que esta definida) y llega como parametro el string nuevo;. La función concatena strings ($file = ‘clases/’ . $fileName.’.php’;) y forma el path clases/nuevo.php, luego pregunta si el archivo existe y si la pregunta es afirmativa termina incluyéndolo. Entonces sin necesidad de incluir el archivo estamos haciendo la instancia de la clase que deseábamos. Otra de las ventajas de usar autoload es que solamente vamos a incluir los archivos que son necesarios para nosotros. Muchas aplicaciones incluyen “kilos” de código innecesariamente sobrecargando la memoria sin sentido