Mostrando entradas con la etiqueta Clases y objetos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Clases y objetos. Mostrar todas las entradas

miércoles, 27 de mayo de 2009

Notas para iniciarse en PHP, Parte 6: Clases y objetos en PHP5


Un nuevo modelo de objetos

En la versión 5 de PHP se introdujo un nuevo modelo de objetos, rehaciendo completamente el anterior, para mejorar el funcionamiento y agregar nuevas características, en beneficio del programador por fortuna.

El cambio de modelos

Como mencionamos en el artículo anterior, la definición de una clase esta integrada por un conjunto de componentes que aparecen dentro de una estructura. Para definir una clase se utilizará nuevamente la palabra "class", seguida del nombre de la clase y la apertura y cierre de corchetes, dentro de los cuales se declararán los componentes de ésta. Hasta aquí no se ve diferencia con el modelo anterior sin embargo, durante la declaración de los componentes de la clase es cuando tenemos los primeros cambios, ya que ahora se toma en cuenta la visibilidad de los campos (también llamados propiedades en el manual) y los métodos, por ejemplo si tomamos la clase Carrito del artículo anterior y la migramos a la versión 5 del lenguaje tenemos un resultado como el que sigue:

class Carrito {
public $items; // Ítems en nuestro carrito de compras

// Agregar $num artículos de $artnr al carrito

public function agregar_item($artnr, $num) {
$this->items[$artnr] += $num;
}

// Tomar $num artículos de $artnr del carrito

public function retirar_item($artnr, $num) {
if ($this->items[$artnr] > $num) {
$this->items[$artnr] -= $num;
return true;
} elseif ($this->items[$artnr] == $num) {
unset($this->items[$artnr]);
return true;
} else {
return false;
}
}
}


Es la clase Carrito con los métodos que ya vimos antes, sin embargo tanto al único campo como a los métodos se les antepuso la palabra public, que es un indicador de visibilidad, esto se debe a que el modelo de objetos de PHP5 tenemos que indicar el tipo de visibilidad de los métodos y propiedades de nuestras clases, la cual especifica el contexto en el que estos componentes podrán ser utilizados. La declaración public indica que el método puede ser utilizado en el contexto en el que se instancie la clase, es decir donde se cree un objeto de esta clase, por ejemplo si instanciamos la clase Carrito:

$carrito = new Carrito;
$carrito->agregar_item("10", 1);
$carrito->agregar_item("8", 3);
$carrito->agregar_item("110", 4);
$carrito->retirar_item("8", 2);


Podemos acceder a los métodos agregar_item o retirar_item debido a que su indicador de visibilidad es public, de haber utilizado protected o private esto no habría sido posible, ya que protected determina que los métodos y propiedades solo pueden ser utilizados dentro de la definición de la propia clase y en la de sus clases extendidas, mientras que private restringe el uso de los métodos y propiedades únicamente a la declaración de la misma clase .

El concepto de visibilidad se introdujo en esta versión es una de las principales diferencias que encontraremos durante el trabajo con las clases, y lo primero a lo que debemos adaptarnos al migrar nuestra programación, el resto del trabajo consiste en conocer las nuevas funcionalidades que ofrece PHP para este modelo.

Instancia de una clase

Como se vio en el ejemplo anterior, la instanciación de clases se sigue haciendo de la misma manera que en la versión 4, solo hay que notar que algunas autores recomiendan que aunque no se pasen parámetros al constructor se incluya el paréntesis durante la instanciación, es decir:

$carrito = new Carrito();
Lo cual es totalmente opcional.

Herencia

El mecanismo para la herencia sigue siendo la extensión de clases con "extends" como en la versión 4.

Autocarga de objetos

Esta es una facilidad que brinda PHP para acceder a los archivos fuente de nuestras clases, que es útil cuando el nombre del archivo es el mismo que el de la clase que contiene. Anteriormente se tenía que realizar un include para cada archivo, mientras que ahora se cuenta con una función que puede hacer ese trabajo por nosotros.

Si declaramos en nuestro programa la función __autoload() PHP la llamará automáticamente cada vez que se haga una nueva instancia a una clase que no ha sido incluida aún, esta función recibe el nombre de la clase como parámetro y dentro de ella podemos realizar la inclusión del archivo, por ejemplo:

<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}

$obj = new MyClass1();
$obj2 = new MyClass2();
?>


En este ejemplo no existe dentro del programa la definición de la clase MyClass1, entonces al hacer la instancia PHP ejecutará por si solo la función __autoload() y le pasará como parámetro el nombre de la clase que estamos llamando, es decir que la función recibirá "MyClass1" en su parámetro $class_name, entonces teniendo el nombre de la clase y sabiendo que es el mismo nombre del archivo que la contiene, podemos incluir este archivo con la función require_once.

Constructores y destructores

La declaración de un constructor ahora es diferente, ya no se utiliza el nombre de la clase para identificarlo sino el nombre especial __contruct, de modo que la función que declaremos con este identificador será llamada cada vez que se cree un nuevo objeto de nuestra clase:

<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}

class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}

$obj = new BaseClass();
$obj = new SubClass();
?>


El ejemplo anterior nos muestra no solo la declaración de un constructor de un clase sino también que el constructor de una clase padre no es llamado implícitamente por el constructor de sus hijos, por lo que tiene que debe ser llamado explícitamente con partent::__contruct(). La principal ventaja de utilizar el mismo nombre para todos los constructores, es que al momento de cambiar el nombre de una clase no tendremos que hacer lo mismo con su contructor.

El método __destruct() es el destructor de la clase, este es un nuevo tipo de método que se incluyó como una de las novedades de PHP5, el cual es ejecutado cuando todas las referencias al objeto de la clase sean removidas, o cuando el objeto sea explícitamente destruido, o cuando llegue la finalización de un programa.

<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();
?>


Este método tampoco es llamado implícitamente en los destructores de las clases hijo, por lo que de querer ejecutar el destructor del padre debería llamarse con parent::__destruct().

Elementos estáticos y constantes

Dentro de los componentes de una clase pueden existir miembros estáticos, es decir propiedades y métodos accesibles fuera de la definición de la propia clase que no necesitan de un objeto para ser utilizados, sino que pueden accederse directamente con el operador de resolución de contexto '::'. Estos componentes estáticos no pueden ser redefinidos en la extensión de una clase.

<?php
class Foo {
public static $prop = "Propiedad estática";
public static function aStaticMethod() {
echo "Método estático que es accesible sin un objeto";
}
}

Foo::aStaticMethod();
echo Foo::$prop;
?>


Para acceder a una propiedad o método estático dentro de la definición de la misma clase, se utiliza la palabra 'self' junto con el operador de resolución de contexto '::' como se indica:

class Foo
{
public static $my_static = 'foo';

public function staticValue() {
return self::$my_static;
}
}


También pueden existir propiedades constantes es decir, valores que no pueden ser alterados y que tienen un identificador para acceder a ellos. Las constantes difieren de las variables normales en que no se usa el símbolo $ para declararlas o usarlas. Para leer sus valores desde el contexto de la definición de la clase se utiliza la palabra 'self' seguida del operador '::' junto con el identificador de la constante. Desde el contexto de la instancia no se puede acceder a las contantes como una propiedad de los objetos, sino que se accede directamente a ellas a través de la clase con el operador de resolución de contexto '::'.

<?php
class MyClass
{
const constant = 'constant value';

function showConstant() {
echo self::constant . "\n";
}
}

echo MyClass::constant . "\n";

$class = new MyClass();
$class->showConstant();
// echo $class::constant; No es una operación permitida
?>
Su modo de acceso es similar al de las propiedades estáticas como se puede ver, con la diferencia de que las propiedades constantes no pueden ser alteradas. En ambos casos si deseamos acceder a una propiedad estática o constante de la clase padre desde un método del hijo, debemos utilizar la palabra 'partent', tal como se vió su uso en las secciones anteriores.

Abstracción de datos e interfaces

Otra de las novedades de esta versión son las clases abstractas, las cuales nos permiten crear clases no instanciables que funcionan como base para las clases hijo que las extenderán. Las clases abstractas pueden contener métodos abstractos, es decir métodos de los que solo aparece su declaración en la clase base y que forsozamente tienen que ser definidos en las clases hijo.

Las clases abstractas son útiles cuando tenemos un grupo de clases que compartirán un comportamiento en común y al mismo tiempo implementarán funcionalidades particulares, para lo cual podemos crear una clase base que indique la estructura general que tendrán todos las clases extendidas. La diferencia con el tipo de herencia común es que las clases abstractas se utilizarán cuando no necesitemos implementar el comportamiento de la clase base dentro de nuestros programas.

Siguiendo con la revisión también nos encontramos que existen interfaces, que consisten en estructuras similares a las clases abstractas ya que no pueden instanciarse, y dentro de su definición también hayamos propiedades y métodos, pero tienen la gran diferencia de que en una interface solo se declaran los métodos, estos nunca se definen, de esta tarea se encargarán las clases que las implementen. Como se pueden dar cuenta, mencione que las clases implementarán las interfaces, y es que aquí se habla de implementación en lugar de extensión, a pesar de ser mecanismos similares se hace la diferencia, ya que en el caso de las interfaces las clases que las implementan nunca heredan un método funcional. Entonces estas estructuras especifican qué métodos deberán poseer las clases que los implementen, y dejarán los detalles de la implementación a estas clases.

Las interfaces tienen gran utilización dentro de los lenguajes orientados a objetos para estandarizar ciertas funciones, al establecer los comportamientos que deberán seguir los objetos que implementen alguna funcionalidad particular. Por ejemplo, dentro de Java se utilizan para implementar el manejo de eventos del usuario, ya que si deseamos que alguna de nuestras clases pueda capturar algún evento del teclado o ratón, necesitan implementar una interfase que proporciona el lenguaje para el manejo de estos eventos, de forma que sin conocer los detalles sobre el manejo de eventos, sabremos que métodos deberemos implementar dentro de nuestras clases para utilizarlo. Algo similar puede verse en otros lenguajes, incluyendo a PHP, para crear frameworks de programación, ya que de esta forma podemos establecer que comportamiento deberá tener una clase que desee implementar cierta funcionalidad contemplada en el armazón.

La palabra reservada 'final'

Cuando se desea que las clases hijo no puedan redefinir un método o modificar el valor de una propiedad de la clase padre, se puede utilizar la palabra reservada final durante su declaración, con la cual PHP interpretará que este valor no podrá ser alterado fuera del contexto de su clase, lanzando un error de querer hacerlo.

Otras funcionalidades

PHP ofrece además de lo anterior algunas opciones adicionales que podemos utilizar con nuestras clases para obtener ciertas facilidades, como son:

El acceso a miembros y sobrecarga de métodos. Se usan para registrar nuevas propiedades en los objetos en tiempo de ejecución y para implementar la sobrecarga de métodos.
La interacción con objetos. Últil para desplegar las propiedades de un objeto desde un foreach.
Patrones. Sirven para implementar funcionalidades comunes en nuestros programas que nos ahorren tiempo de programación.
Clonación de objetos. Nos permite hacer replicas de nuestros objetos.

Y con esta última revisión termino con mis anotaciones sobre el modelo de objetos de PHP5, ahora lo que queda por ver es la forma en la que podemos utilizar todo lo aprendido interactuando con las interfaces de usuario, para lo que revisaremos la lectura de datos de formularios, el manejo de sesiones y cookies, y algunas funciones que hacen varias cosas interesantes.

Hasta la próxima.



jueves, 21 de mayo de 2009

Notas para iniciarse en PHP, Parte 5: Clases y objetos en PHP4


Todo gran viaje comienza con un solo paso

La utilización de clases y objetos

PHP nos permite programar tanto de manera procedimental como con orientación a objetos, es en está última donde surgen los conceptos de clases y objetos, a los cuales nos enfocaremos en este artículo. En el viejo estilo estructurábamos nuestros programas en un conjunto de funciones que utilizábamos dentro de las secuencias de ejecución, en la programación orientada a objetos nuestras aplicaciones constarán de un conjunto de elementos llamados objetos, los cuales interactúan entre sí por medio de sus métodos. Un método es similar a una función en programación estructurada, solo que éste se encuentra asociado a un objeto específico, el cual utiliza los métodos para implementar su funcionalidad particular. Todos los objetos están basados en la definición de una clase, la cual puede verse como la estructura que especifica las características y el comportamiento que tendrá el objeto; todo objeto debe pertenecer a una determinada clase, la clase entonces es una entidad pasiva que define que hará el objeto, mientras el objeto es una entidad activa que realizará un conjunto de tareas dentro de nuestros programas. Si por ahora no ha quedado muy clara esta definición quizás con la implementación en el lenguaje se entenderá mejor.

Clases y objetos en PHP4

En PHP una clase es una colección de variables y funciones que trabajan con estas variables. El concepto de clase y objeto se introdujo en este lenguaje a partir de su versión 4, contando ya con conceptos como la herencia y los constructores, componentes que representan el mínimo necesario para considerarla orientada a objetos, sin embargo este modelo se queda corto si lo comparamos con otros lenguajes basados en objetos más maduros y versátiles como Java o C#. El modelo de la versión 4 es más parecido al de otros lenguajes basados en scripts como Javascript o Actionscript. Por todo lo anterior en la versión 5 del lenguaje se introdujoun nuevo modelo de objetos, que incluyera un mejor funcionamiento y más opciones, para el programador, pero aún así, las bases para el trabajo con clases y objetos siguen siendo los de la versión 4, por lo que es importante al iniciarse en este tema dar un repaso a las características que ofrece esta versión, además de que muchas bibliotecas libres que encontramos por la red siguen utilizándola.

Definición de una clase

La definición de una clase en PHP se realiza por medio de la palabra reservada class seguida de la apertura y cierre de llaves utilizada en las estructuras, dentro de estas llaves se escriben las variables y funciones que contiene una clase. Siguiendo una sintaxis como la que sigue:
class NombreDeLaClase {
...
}


El siguiente ejemplo nos muestra una implementación para esta sintaxis:
<?php
class Carrito {

var $items; // Ítems en nuestro carrito de compras

// Agregar $num artículos de $artnr al carrito

function agregar_item($artnr, $num) {
$this->items[$artnr] += $num;
}

// Tomar $num artículos de $artnr del carrito

function retirar_item($artnr, $num) {
if ($this->items[$artnr] > $num) {
$this->items[$artnr] -= $num;
return true;
} elseif ($this->items[$artnr] == $num) {
unset($this->items[$artnr]);
return true;
} else {
return false;
}
}
}
?>

En este ejemplo aparece la definición de la clase Carrito, que posee una variable que contendrá un arreglo asociativo, además la clase dispone de dos funciones o métodos para agregar o eliminar items del arreglo, en un sentido más abstracto la clase representa un objeto del mundo real, en este caso un carrito de compras, al que le podemos agregar o quitar productos.

Las variables o campos (como se les llama en el lenguaje Java) de las clases se identifican anteponiendo la palabra reservada var, estos campos se pueden acceder desde cualquiera de las funciones de la clase, como si se tratara de variables globales dentro del contexto de la definición de la clase, anteponiendo al nombre del campo la expresión $this->, lo cual es muy importante ya que de otra forma PHP interpretara que se trata de una variable local de las funciones en las que se está haciendo referencia. Las funciones que aparecen, que tienen la misma definición que en la programación estructurada que vimos en artículos anteriores, reciben el nombre de métodos, que es el término que emplearemos de ahora en adelante, ya que es el nombre que reciben en la programación orientada a objetos, debido a que a través de ellos se define el comportamiento de los objetos que se crean con las definiciones de nuestras clases. Para accesar a un método contenido en la definición de la clase también se utliza la expresión $this-> seguida de la llamada al método, por ejemplo si se incluyera un tercer método en la clase Carrito el cual necesitara utilizar alguno de los otros:
   //Vaciar el carrito de compras eliminando todos los artículos

function vaciar_carrito() {
foreach ($this->items as $artnr=>$num)
$this->retirar_item($artnr, $num);
}
En el ejemplo, el nuevo método vaciar_carrito() utilizará la funcionalidad de retirar_item() para eliminar todos los elementos del arreglo.

Los métodos le permiten a los objetos interactuar con su entorno, para lo cual es necesario primero crear un objeto con la definición de la clase, lo cual es llamado instanciar una clase, y se hace de la siguiente forma:

$objeto = new NombreDeLaClase;


La instancia de la clase se realiza con la expresión new seguida del nombre de la clase y está debe ser asignada a una variable, la cual será el objeto creado a partir de esta instancia. El nombre de la clase puede llevar paréntesis si así lo deseamos, pero su uso es opcional a menos que la definición del constructor de la clase indique que tiene que recibir algún de valor, para lo cual se vuelve obligatorio el uso de paréntesis en la instanciación, el tema de los constructores lo veremos más adelante.

Siguiendo con el ejemplo del carrito de compras la instancia de la clase Carrito se realizaría de la siguiente manera:
$carrito = new Carrito;
$carrito->agregar_item("10", 1);

$otro_carrito = new Carrito;
$otro_carrito->agregar_item("0815", 3);

En el ejemplo anterior se realizan dos instancias de la clase Carrito por medio de la expresión new Carrito, con lo cual se crean los objetos $carrito y $otro_carrito, los cuales una vez creados pueden acceder a los métodos de esta clase -tanto agregar_item como retirar_item).

Herencia

Las clases pueden tener puntos de intersección en su definición, como suele ser el caso de compartir campos o métodos similares o idénticos, en ese caso sería un gasto de tiempo innecesario volver a escribir estos métodos y campos en la definición en cada clase en las que se vayan a utilizar, para facilitar la solución de este problema PHP permite la herencia entre la clases, la cual consiste en que a partir de una clase original se pueden crear otro grupo de clases que contengan los métodos y campos de la primera, pero sin tener que reescribirlos en cada una de ellas, la clase original es llamada padre mientras que a las clases derivadas se les llama hijos, entonces estos componentes son heredados de la clase padre a las clases hijo, con lo cual se consigue que compartan la definición de los campos y métodos del padre, pudiendo además incorporar nuevas funcionalidades a las ya heredadas. PHP implementa la herencia gracias a un mecanismo llamado extensión, este nombre viene del hecho de que por lo general las clases hijo extienden la funcionalidad de la clase padre, al redefinir antiguos métodos o incluir nuevos métodos y campos. Las clases extendidas o clases hijo deben incluir en su definición la palabra reservada 'extends'.
<?php
class Carrito_Con_Nombre extends Carrito {
var $duenyo;

function definir_duenyo ($nombre) {
$this->duenyo = $nombre;
}
}
?>

En este ejemplo se define una nueva clase llamada Carrito_Con_Nombre el cual extiende la clase Carrito, ya que hereda de ella todos sus métodos y campos y, además cuenta con un campo y método propio, entonces un objeto de la clase Carrito_Con_Nombre consistirá en un carrito con los métodos agregar_item como retirar_item que además posee un dueño que puede ser definido con el método definir_duenyo, como podemos verlo al instanciar la nueva clase en el siguiente ejemplo:
<?php
$carrito_n = new Carrito_Con_Nombre; // Crear un carrito con nombre
$carrito_n->definir_duenyo("kris");// Nombrar el carrito
print $carrito_n->duenyo; // imprimir el nombre del dueño
$carrito_n->agregar_item("10", 1); // (funcionalidad heredada de carrito)
?>


Vemos que el objeto posee los métodos de la clase padre y los métodos de su propia clase.

Constructores

El constructor de una clase es un método especial que podemos incluir en su definición, el cual es llamado al momento de hacer una nueva instancia de la clase con new, por ejemplo podemos definir un constructor para una nueva clase que extienda a Carrito de la siguiente forma:
<?php
class Auto_Carrito extends Carrito {
function Auto_Carrito() {
$this->agregar_item("10", 1);
}
}
?>


El constructor debe llevar el mismo nombre de la clase a la que pertenece, como vemos en el ejemplo, el cual nos indica que al crearse una nueva instancia de la clase Auto_Carrito, se incluirá por defecto un primer item en el arreglo al emplear el método heredado agregar_item dentro del constructor.

Un constructor puede además incluir uno o más parámetros en su definición, de forma que podamos enviar valores a este método al instanciar un nuevo objeto de la clase:

<?php
class Constructor_Carrito extends Carrito {
function Constructor_Carrito($item = "10", $num = 1) {
$this->agregar_item ($item, $num);
}
}

// Comprar lo mismo de antes.
$carrito_predeterminado = new Constructor_Carrito;

// Comprar esta vez en serio...
$carrito_diferente = new Constructor_Carrito("20", 17);
?>


En el ejemplo dentro de la definición se utilizaron parámetros con valores por defecto, por lo cual no es necesario que en la primera instancia($carrito_predeterminado) se incluyan nuevos valores, para obtener un resultado diferente vemos que en la segunda instancia($carrito_diferente) ya se incluyen los valores dentro de los paréntesis, de forma que ahora el constructor los incluirá dentro del arreglo asociativo.

Los constructores son muy útiles si deseamos asegurarnos que los campos nuestras clases tengan un valor inicial al crearse una nueva instancia de estas.

Operador de resolución de contexto (::)

El operador :: nos permite acceder a un campo o método de una clase sin la necesidad de tener que hacer una instanciación, como sucede en el siguiente ejemplo:
<?php
class A {
function ejemplo() {
echo "Soy la función original A::ejemplo().<br />\n";
}
}

class B extends A {
function ejemplo() {
echo "Soy la función redefinida B::ejemplo().<br />\n";
A::ejemplo();
}
}

// no hay un objeto de la clase A.
// esto imprime
// Soy la función original A::ejemplo().<br />
A::ejemplo();

// crear un objeto de clase B.
$b = new B;

// esto imprime
// Soy la función redefinida B::ejemplo().<br />
// Soy la función original A::ejemplo().<br />
$b->ejemplo();
?>


El ejemplo anterior llama al método ejemplo() en la clase A, pero no hay un objeto de la clase A, así que no podemos escribir $a->ejemplo() o algo semejante. En su lugar llamamos ejemplo() como un 'método de clase', es decir, un método de la clase misma, no de un objeto de tal clase.

Acceso a los campos y métodos del padre (parent)

Al utilizar herencia o extensión de clases con 'extends', ya nos es posible acceder a los métodos y campos de la clase dentro de los nuevos métodos que definimos en las clases hijo, sin embargo, al realizar una refinación o especialización de un método ya existente, a veces resulta necesario utilizar la funcionalidad del método original antes de incluir nuestros cambios.

Para acceder a un método del padre se utiliza la palabra reservada 'parent' seguida del operador de resolución de contexto (::), acompañado con la referencia al método deseado.

En el siguiente ejemplo se muestra la refinación del método ejemplo() en el cual además de incluirse una nueva funcionalidad se hace uso de la funcionalidad del método original:

<?php
class A {
function ejemplo() {
echo "Soy A::ejemplo() y ofrezco funcionalidad básica.<br />\n";
}
}

class B extends A {
function ejemplo() {
echo "Soy B::ejemplo() y ofrezco funcionalidad adicional.<br />\n";
parent::ejemplo();
}
}

$b = new B;

// Esto hace la llamada a B::ejemplo(), la cual llama a su vez a A::ejemplo().
$b->ejemplo();
?>


A pesar de que los hijos heredan los métodos del padre, al volverlos a definir se hace necesaria una forma para diferenciar el método original del refinado, ya que si nos fijamos en el ejemplo, de haber utilizado la expresión $this->ejemplo() PHP interpretaría que se hace referencia al método del hijo, el cual ya cambió y no se obtendría el resultado deseado, además de que se crearía una problema recursivo.

Con este último ejemplo es que concluye el último artículo de esta semana, para el siguiente veremos las diferencias que tiene la versión 5 del lenguaje para el manejo de objetos y las nuevas funcionalidades que nos ofrece.

Hasta la próxima.

miércoles, 25 de febrero de 2009

Otra forma de acceder a la base de datos con PHP


En un de los ejemplos anteriores creamos una clase en PHP para generar documentos XML que contuvieran los resultados de las consultas a una base de datos, en esta ocasión, crearé otra clase en PHP pero que, -como dice el título de este post- se encargará de manejar el acceso a la base de datos, ¿por qué? primero que nada recordemos que en las escuela nos hablaron de la importancia de separar nuestros proyectos de programación con bases de datos en tres capas: presentación, negocio y acceso a datos, y no solo porque de esta forma se ven más elaborados sino por el hecho de que así ya partimos de un muy buen principio de diseño, que ayudará a lograr un mejor proyecto de desarrollo. Por otro lado encontramos que al trabajar con capas nos ahorramos mucho esfuerzo de teclear código, además de que se facilita el mantenimiento de nuestros programas.


El gran beneficio: La principal ventaja que encuentro al utilizar capas en nuestro desarrollo es que al producirse cambios en los requerimientos de nuestros sistemas, realizar modificaciones al código resulta mucho más sencillo. Por ejemplo, si tenemos cambiar de sistema manejador de base de datos de MySQL a MS SQL Server, dentro de nuestro código solo tendremos que realizar cambios en la capa de acceso a bases de datos, dejando prácticamente intacto el resto del código, ya que veamos el ejemplo se entenderá bien como es esto.

Puntos previos a tener en cuenta

Como consideraciones previas, tendremos en cuenta que las responsabilidades que recaerán sobre esta clase son:
  • Conectarse a la base de datos.
  • Acceder a la base de datos.
  • Ejecutar consultas SQL.
  • Insertar registros.
  • Actualizar registros.
  • Borrar registros.
  • Desconectarse de la base de datos.
Al momento de traducir estos requerimientos a la programación, irán surgiendo una serie de campos y métodos que se utilizarán para realizar las tareas necesarias.

Así, las funciones mysql (mysql_connect, mysql_select_db, mysql_query, etc.) que antes se utilizaban dentro del código, serán encapsuladas en esta serie de métodos.

Nota: Al referirme a las clases y objetos utilizo términos que aprendí durante mi incursión en el lenguaje Java, por lo que surgen algunas diferencias con los que manejan los desarrolladores más asiduos de .Net, en el que se refieren a propiedades y atributos de las clases en lugar de campos como en Java, pero ya que el modelo de programación con objetos de PHP5 se apegó más a Java creo que esta terminología queda bastante bien :D

Construyamos nuestra nueva clase para acceso a datos

Siguiendo con el trabajo, ahora les presento la clase que idee para cumplir el objetivo de manejar el acceso a una base de datos -un poco larga la verdad :P-:


<?php
//Clase Acceso a Datos
class AccesoADatos{
//Campos de la clase
private $conexion;
private $ipServidor;
private $usuario;
private $contrasenia;
private $baseDeDatos;
private $resultados;
private $mensaje;
private $registrosLeidos;
//Constructor de la clase
function __construct($ipServidor, $usuario, $contrasenia, $baseDeDatos) {
$this->ipServidor = $ipServidor;
$this->usuario = $usuario;
$this->contrasenia = $contrasenia;
$this->baseDeDatos = $baseDeDatos;
$this->conexion = false;
$this->registrosLeidos = 0;
$this->resultados = array();
$this->mensaje = "";
}
//Destructor de la clase Acceso a Datos
function __destruct() {
$this->liberaResultado();
$this->cierraConexion();
}
//Abre la conexión con el servidor de base de datos. Devuelve falso si hubo error
public function abreConexion() {
$estadoDeConexion = true;
$this->conexion = @mysql_connect($this->ipServidor,$this->usuario,$this->contrasenia);
if(mysql_select_db($this->baseDeDatos,$this->conexion)) {
mysql_query("SET NAMES 'utf8'",$this->conexion);
$estadoDeConexion = true;
}
else {
$estadoDeConexion = false;
$this->estableceError("No fue posible acceder a la base de datos, consulte con el administrador de la base de datos para solucionarlo.");
}
return $estadoDeConexion;
}
//Establece el mensaje de error e inicializa los contadores de resultados
private function estableceError($tipo) {
$this->registrosLeidos = 0;
$this->mensaje = "Error de acceso a datos: ".$tipo;
}
//Ejecuta una consulta SQL contenida en una cadena
public function consulta($consultaSQL, $idResultado = "") {
$estado = true;
if($this->conexion) {
$r = mysql_query($consultaSQL,$this->conexion);
if(mysql_errno() > 0) {
$this->estableceError("No se completó la operación solicitada ".mysql_error());
$estado = false;
}
else
$this->registrosLeidos = mysql_num_rows($r);
if($idResultado == "")
$this->resultados[0] = $r;
else
$this->resultados[$idResultado] = $r;
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
$estado = false;
}
return $estado;
}
//Borra él o los registros de una tabla que cumplan los parámetros recibidos
public function borraRegistro($tabla, $condicion = "1") {
$estado = true;
if($this->conexion) {
$comando = "DELETE FROM `$tabla` WHERE $condicion";
mysql_query($comando,$this->conexion);
if($this->numeroDeError = mysql_errno() > 0) {
$this->estableceError("No se completó la operación solicitada ".mysql_error());
$estado = false;
}
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
$estado = false;
}
return $estado;
}
//Inserta un registro en la tabla específicada utilizando como valores el arreglo de datos recibido
public function insertaRegistro($tabla, $datos) {
if($this->conexion) {
$comando = "INSERT INTO `$tabla` (";
foreach($datos as $llave=>$dato) {
$campos[]= "`$llave`";
$valores[] = $dato;
}
$comando.= implode(",",$campos).") VALUES('".implode("','",$valores)."')";
mysql_query($comando,$this->conexion);
if(mysql_errno($this->conexion) > 0)
$this->estableceError("No se completó la operación solicitada ".mysql_error());
return mysql_affected_rows($this->conexion);
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
return 0;
}
}
//Actualiza el registro de una tabla utilizando los párametors recibidos
public function actualizaRegistro($tabla, $nuevosDatos, $campoLlave) {
if($this->conexion) {
$comando = "UPDATE `$tabla` SET ";
foreach($nuevosDatos as $llave=>$dato)
$nuevosValores[] = "`$llave` = '$dato'";

foreach($campoLlave as $llave=>$dato)
$condicion[] = "`$llave` = '$dato'";
$comando.= implode(",",$nuevosValores)." WHERE ".implode(" AND ",$condicion);
mysql_query($comando,$this->conexion);
if(mysql_errno($this->conexion) > 0)
$this->estableceError("No se completó la operación solicitada ".mysql_error());
return mysql_affected_rows($this->conexion);
}
else {
$this->estableceError("Conexión con la base de datos no establecida.");
return 0;
}
}
//Regresa el contenido de un registro del resultado en un arreglo
public function devuelveArreglo($idResultado = "") {
if($idResultado=="")
$idResultado = 0;
if($this->resultados[$idResultado])
return mysql_fetch_assoc($this->resultados[$idResultado]);
else
return false;
}
//Devuelve el número de registros leidos por la última consulta
public function devuelveRegsLeidos() {
return $this->registrosLeidos;
}
//Devuelve el utimo mensaje de error generado
public function devuelveError() {
return $this->mensaje;
}
//Libera de la memoria del servidor el resultado de cualquier consulta realizada
public function liberaResultado() {
foreach($this->resultados as $llave=>$r) {
if($this->resultados[$llave])
mysql_free_result($this->resultados[$llave]);
}
$this->resultados = array();
}
//Cierra la conexión con el servidor de base de datos
public function cierraConexion() {
if($this->conexion){
mysql_close($this->conexion);
$this->conexion = false;
}
}
}
?>

La clase recibe el nombre de AccesoADatos, y dentro de esta clase aparece un primer método identificado como __construct, que es el método constructor de la clase, es llamado por PHP al momento de instanciarla. En caso de no declararlo, PHP llamaría a un constructor por defecto que poseen todas las clases, como en el caso de la clase ConsultaXML que programamos en uno de los posts anteriores.
function __construct($ipServidor, $usuario, $contrasenia, $baseDeDatos) {
$this->ipServidor = $ipServidor;
$this->usuario = $usuario;
$this->contrasenia = $contrasenia;
$this->baseDeDatos = $baseDeDatos;
$this->conexion = false;
$this->registrosLeidos = 0;
$this->resultados = array();
$this->mensaje = "";
}


Dentro del constructor se reciben los parámetros necesarios para realizar la conexión con el servidor de base de datos, y para acceder a la base de datos seleccionada. Además, se inicializan los campos que se utilizarán dentro de los métodos de la clase.

En el siguiente método encontramos el identificador __destruct:

function __destruct() {
$this->liberaResultado();
$this->cierraConexion();
}


El cual representa el destructor de la clase que es llamado automáticamente por PHP, al momento que todas las referencias a un objeto de la clase AccesoADatos sean removidas o cuando el objeto sea explícitamente destruido, o en cualquier orden en la finalización de la ejecución. Dentro de éste método se liberará la memoria de cualquier resultado de las consultas realizadas, para finalmente cerrar la conexión con el servidor de base de datos.

El método abreConexion() realiza la conexión con el servidor de base de datos MySQL, utilizando los parámetros $ipServidor, $usuario y $constrasenia, establecidos en el constructor de la clase.

mysql_connect($this->ipServidor,$this->usuario,$this->contrasenia);
if(mysql_select_db($this->baseDeDatos,$this->conexion)) {
mysql_query("SET NAMES 'utf8'",$this->conexion);
$estadoDeConexion = true;
}

Inmediatamente después accede a la base de datos especificada por el campo $baseDeDatos, y finalmente establece que el juego de caracteres que se utilizará en las consultas es UTF-8 a través de la consulta "SET NAMES utf8".

Todos los mensajes de error que se generen con las operaciones de acceso a la base de datos se establecerán con el método estableceError(), y estos mensajes podrán ser consultados desde fuera de la clase a través del método devuelveMensaje().

El método consulta(), recibe una cadena dentro del parámetro $consultaSQL que representa una consulta SQL para ejecutarla con la función mysql_query(),

$r = mysql_query($consultaSQL,$this->conexion);
if(mysql_errno() > 0) {
$this->estableceError("No se completó la operación solicitada ".mysql_error());
$estado = false;
}
else
$this->registrosLeidos = mysql_num_rows($r);
if($idResultado == "")
$this->resultados[0] = $r;
else
$this->resultados[$idResultado] = $r;


almacenando el resultado de la consulta dentro del arreglo $resultados, identificandola con el parámetro $idResultado que puede ser tanto una cadena como un valor numérico. En caso de que no se reciba un valor que identifique el resultado, este se almacena dentro del arreglo como el elemento 0 en el índice.

El método borraRegistro() eliminará uno o más registros en una tabla especificada, dependiendo de los parámetros que reciben, dentro de los que se incluye el nombre de la tabla y la condición del comando SQL.

El métodos insertaRegistro(), realiza la inserción de un registro en la tabla especificada utilizando los valores contenidos en el arreglo asociativo $datos, el cual debe poseer el formato 'llave'=>"valor", donde la llave representa el nombre del campo de la tabla y el valor es el dato que será insertado en este campo.

$comando = "INSERT INTO `$tabla` (";
foreach($datos as $llave=>$dato) {
$campos[]= "`$llave`";
$valores[] = $dato;
}
$comando = $comando.implode(",",$campos).") VALUES('".implode("','",$valores)."')";



Utilizando el ciclo foreach y la función implode para construir el comando INSERT que será ejecutado.

El método actualizaRegistro() es similar a insertaRegistro(), solo que además del nombre de la tabla y del arreglo de datos, se recibe segundo arreglo asociativo llamado $campoLlave, que se utiliza para identificar él o los registros que serán actualizados por este método.

El método devuelveArreglo(), nos permite acceder a cada uno de los renglones del resultado de una consulta identificado por $idResultado, almacenado este renglón dentro de un arreglo asociativo por medio de la función mysql_fetch_assoc.

Con este código crearemos el archivo AccesoADatos.php, que a partir de ahora utilizaremos en los siguientes ejemplos para acceder a nuestras bases de datos. Cómo una muestra del uso de esta nueva clase, refactorizaremos el código PHP del último ejemplo, es decir el archivo consultaMySQL.php, para generar un nuevo archivo PHP que posea con la siguiente contenido:


<?php
require("ConsultaXML.php"); //Referencia al archivo que contiene la clase ConsultaXML
require("AccesoADatos.php");//Referencia al archivo que contiene la clase AccesoADatos
$consultaXML = new ConsultaXML();//Creación del documento XML
//Parámetros para la conexión a la base de datos
$servidor = "localhost";
$usuario = "root";
$contrasenia = "[TuConstraseña]";
$baseDeDatos = "obras_literarias";
$acceso = new AccesoADatos($servidor,$usuario,$contrasenia,$baseDeDatos);
if($acceso->abreConexion()) { //Accedemos a la base de datos
//Comprobamos que los datos se enviaron correctamente
if(isset($_POST['txtNombre']) && ($nombre = stripslashes(trim($_POST['txtNombre'])))!="") {
$consulta = "SELECT * FROM autores WHERE Nombre LIKE '%".addslashes($nombre)."%'";
if($acceso->consulta($consulta)) {//Realizamos la consulta y comprobamos el resultado
if($acceso->devuelveRegsLeidos() > 0){//Comprobamos si se encontraron registros
//Creamos el documento XML con el resultado de la consulta
$consultaXML->iniciaDocumento();
$consultaXML->startElement("Tabla");
$consultaXML->writeAttribute("nombre","autores");
while($renglon = $acceso->devuelveArreglo()) {//Extrae un renglon del resultado como un arreglo asosciativo
$consultaXML->creaRegistroDesdeArreglo($renglon, "Id");
}
$consultaXML->endElement();
$consultaXML->salida();
}
else{
$mensajeDeError = "Autor no encontrado";
}
}
else {
$mensajeDeError = $acceso->devuelveError();
}
}
else {
$mensajeDeError = "Datos imcompletos";
}
}
else {
$mensajeDeError = $acceso->devuelveError();
}
if($mensajeDeError != "") {//En caso de que exista algún mensaje de error lo devolvemos como respuesta
$consultaXML->iniciaDocumento("Error");
$consultaXML->text($mensajeDeError);
$consultaXML->salida();
}?>

El funcionamiento seguirá siendo exactamente el mismo, pero como vemos la cantidad de líneas de código y de variables se reduce, gracias al encapsulamiento de la clase. Y la parte más importa es, que nos abstraemos de la mayoría de los detalles en cuanto al acceso a la base de datos, por lo que en caso de cambiar de sistema manejador de base de datos, o agregar nuevas funcionalidades al acceso a datos, solo deberemos modificar esta clase.

En los siguientes ejemplos veremos como utilizar el resto de los métodos la clase AccesoADatos, programando una interface en Ajax y Javascript mucho más completa.

Hasta la próxima.

miércoles, 4 de febrero de 2009

Refactorizar el código que procesa XML, Parte 1


Los tres pasos de la buena programación

En el post anterior utilizamos un método para generar código XML del lado de PHP bastante simple, que funciona bien para el ejemplo debido la cantidad de datos que estábamos utilizando, sin embargo, al incrementarse el tamaño del documento XML generado, utilizar solamente llamadas a la función echo resultaría en un código fuente bastante largo y difícil de mantener, además la creación de documentos XML desde PHP se llevará a cabo en más de un programa, por lo que una forma de agilizar este proceso consiste en crear bloques de código reutilizables, que nos ahorren una parte considerable del trabajo al teclear nuestros programas. Una forma de hacerlo es utilizando grupos de funciones, cada una correspondiendo a un paso específico del proceso, otra forma sería el uso de clases para manejar el proceso completo, lo cual implicaría adentrarnos un poco en programación orientada a objetos en este lenguaje. Para la generación del documento XML diseñaremos una sencilla clase en PHP, utilizando el modelo de objetos de su versión 5. De esta forma la escritura de los siguientes programas será más ágil y sobre todo más fácil de mantener.


Consideraciones previas

La principal cuestión antes de continuar es conocer que formato tendrán los documentos XML que crearemos para los siguientes ejemplos, debido a que el próximo paso será el manejo de las bases de datos, consideraremos que los elementos del documento XML representarán los registros y campos obtenidos al realizar consultas a las bases de datos relacionales. Sería algo como lo que sigue:

<?xml version="1.0" encoding="utf-8"?>
<Consulta>
<Tabla nombre="Autores">
<Registro id="1">
<Campo nombre="Nombre">Juan Rulfo</Campo>
<Campo nombre="Nacionalidad">Mexicano</Campo>
<Campo nombre="Profesion">Novelista</Campo>
</Registro>
<Registro id="2">
<Campo nombre="Nombre">Pablo Neruda</Campo>
<Campo nombre="Nacionalidad">Chileno</Campo>
<Campo nombre="Profesion">Poeta</Campo>
</Registro>
<Registro id="3">
<Campo nombre="Nombre">Miguel de Cervantes Saavedra</Campo>
<Campo nombre="Nacionalidad">Español</Campo>
<Campo nombre="Profesion">Novelista, Poeta y Dramaturgo</Campo>
</Registro>
</Tabla>
</Consulta>


Así, los elementos principales estarán representados por la etiqueta Tabla, y cada tabla tendrá un identificador que será único, representado en este caso por el atributo nombre, que representa el nombre de la tabla dentro de la base de datos. En el ejemplo existe únicamente la tabla Autores, dentro de la cual encontramos un grupo de registros, los cuales se identifican por el atributo id. Dentro de todo registro, aparecerá una serie de elementos llamados Campos, que serán identificados por el atributo nombre, y los valores de cada campo están contenidos en sus propias etiquetas.

Refactorización del lado del Servidor

De forma que el código en PHP necesario para crear este tipo de documentos será el siguiente:


<?php
//Clase creada para manejar documentos XML que tengan el formato de una consulta a una base de datos
class ConsultaXML extends XMLWriter{
//Función que inicializa el documento XML, establece el contenido de su encabezado y abre el elemento raíz
function iniciaDocumento($elementoRaiz = "Consulta", $version = "1.0", $juegoDeCarateres = "UTF-8") {
$this->openMemory();
$this->setIndent(true);
$this->setIndentString(" ");
$this->startDocument($version,$juegosDeCaracteres);
$this->startElement($elementoRaiz);
}
/*
* Método que crea el elemento Campo dentro del documento, recibiendo como parámetros el nombre del campo
* y el valor que contendrá.
*/
public function creaCampo($nombreDelCampo, $valor) {
$this->startElement("Campo");
$this->writeAttribute("nombre",$nombreDelCampo);
$this->text($valor);
$this->endElement();
}
/*
* Método que crea el elemento Registro dentro del documento, recibiendo como parámetros el arreglo asociativo
* que contendrá los nombres y valores de los campos que poseerá el registro, junto con el identificaor del
* registro que será almacenado en su atributo "id".
*/
public function creaRegistroDesdeArreglo($arreglo, $idRegistro) {
$this->startElement("Registro");
$this->writeAttribute("id",$idRegistro);
if(is_array($arreglo)){
foreach ($arreglo as $llave => $valor){
$this->creaCampo($llave, $valor);
}
}
$this->endElement();
}
/*
* Método que crea el elemento Tabla dentro del documento, recibiendo como parámetros una matriz de arreglos
* asociativos, el nombre de la tabla y el nombre del campo que identificará a los registros que contendrá la
* tabla. Dentro de un ciclo se llamará a al método creaRegistro para cada uno de los arreglos asociativos
* que contenga la raíz, de esta forma se creará un documento XML que contenga todos los registros de una
* tabla obtenida en una consulta.
*/
public function creaTablaDesdeMatriz($matriz, $nombreDeLaTabla, $campoIdRegistro) {
$this->startElement("Tabla");
$this->writeAttribute("nombre",$nombreDeLaTabla);
if(is_array($matriz) && gettype($campoIdRegistro)=="string"){
foreach ($matriz as $llave => $renglon){
if($campoIdRegistro == "")
$this->creaRegistroDesdeArreglo($renglon, $llave);
else
$this->creaRegistroDesdeArreglo($renglon, $renglon[$campoIdRegistro]);
}
}
$this->endElement();
}

//Cierra el elemento raíz y finaliza el documento, regresando además el contenido del documento actual.
public function finalizaDocumento(){
$this->endElement();
$this->endDocument();
return $this->outputMemory();
}

/**
* Establece que el programa PHP actual generará como salida un archivo XML y envía a esta la salida el contenido del
* documento creado.
*/
public function salida(){
header('Content-type: text/xml');
echo $this->finalizaDocumento();
}
}
?>


Así, nuestra clase recibirá el nombre de ConsultaXML, ya que el propósito de ésta clase es crear documentos XML que representen conjuntos de datos estructurados, como lo que se obtienen de las consultas a bases de datos o a los archivos estructurados, o cualquier otro tipo de información que necesite de un formato similar.

Esta clase hereda sus métodos y propiedades de la clase predefinida XMLWriter, lo cual está indicado por la sentencia class ConsultaXML extends XMLWritter. Utilizamos esta clase predefinida porque ya posee todos los métodos necesarios para escribir documentos XML con etiquetas y atributos, adicionando además un control de errores para prevenir que los documentos creados contengan errores en su estructura. Otro punto importante es que XMLWriter permite dirigir el salida completa del archivo tanto a la memoria como al disco duro, de esta forma podemos utilizar la función echo para dirigir esta salida como respuesta del servidor al navegador, tal y como lo hicimos en el último ejemplo al respecto.

El primer método que encontramos en la clase es iniciaDocumento:
$this->openMemory();
$this->setIndent(true);
$this->setIndentString(" ");
$this->startDocument($version,$juegosDeCaracteres);
$this->startElement($elementoRaiz);

el cual deberá ser el primer método que se llama para crear el documento XML, en éste se establece que el nuevo objeto XMLWrite tendrá una salida en forma de cadena en la memoria, a través de la sentencia $this->openMemory(), se establece que existirá indentación hacia la derecha en el documento con $this->setIndent(true) y con $this->setIndentString(" ") definimos que el caracter espacio en blanco será utilizada para esta indentación. Se establece además el encabezado del documento, y finalmente se crea el primer elemento del documento, que es indicado por el parámetro $elementoRaiz. Como también podemos apreciar, en los parámetros se definen valores por defecto para facilitarnos el uso de este método, ya que éstos serán los que requeriremos en la mayoría de los ejemplos, teniendo siempre la posibilidad de establecer nuevos valores.

Los métodos creaCampo, creaRegistro y creaTabla llevarán a cabo toda la tarea de llenar el contenido del documento XML, permitiendo cada una de ellas crear los respectivos elementos Campo, Registro y Tabla, como habíamos establecido en las consideraciones previas.

El método finalizaDocumento cerrará la etiqueta del primer elemento que fue creado con iniciaDocumento, además finaliza el documento y devuelve todo su contenido como respuesta.

$this->endElement();
$this->endDocument();
return $this->outputMemory();

Esta respuesta puede ser recibida de diferentes formas, ya sea siendo almacenada en una variable como una cadena de texto, o mandada a alguna función para crear un archivo, o puede ser enviada a la función echo para el propósito que ya se describió, lo cual es precisamente lo que se hace en el siguiente método:
public function salida(){
header('Content-type: text/xml');
echo $this->getDocument();
}

Que antes de mandar el contenido como salida del programa, establece que el tipo de salida será la de un archivo XML, para que el navegador tenga conocimiento y la trate como tal.

Este código puede ser incluido dentro del script donde se vaya a llevar a cabo la generación del documento XML, o puede ser almacenado en un archivo .php aparte que será llamado por medio de la función require, lo cual personalmente recomiendo para facilitar su reutilización en otros programas. Para este ejemplo el código se almacenará en una archivo llamado ConsultaXML.php.

Implementación del nuevo código

Ahora pasemos a lo bueno, que es utilizar esta clase, para lo que escribiremos un nuevo programa en PHP cuyo archivo recibirá el nombre de generaXMLConClase.php, y creará un documento a partir de una matriz, conformada por arreglos asociativos. El código es el siguiente:


<?
require("PruebasPHP/ConsultaXML.php"); //Referencia al archivo que contiene la clase ConsultaXML
//Arreglos que representan la información del documento XML
$registro1 = array('idAutor'=>1,'Nombre'=>"Juan Rulfo",'Nacionalidad'=>"Mexicano",'Profesion'=>"Novelista");
$registro2 = array('idAutor'=>2,'Nombre'=>"Pablo Neruda",'Nacionalidad'=>"Chileno",'Profesion'=>"Poeta");
$registro3 = array('idAutor'=>3,'Nombre'=>"Miguel de Cervantes Saavedra",'Nacionalidad'=>"Español",'Profesion'=>"Novelista, Poeta y Dramaturgo");
$tabla = array($registro1, $registro2, $registro3);
//Creación del documento XML
$consultaXML = new ConsultaXML();
$consultaXML->iniciaDocumento();
$consultaXML->creaTablaDesdeMatriz($tabla, "Autores", "idAutor");
$consultaXML->salida();
?>


Nota: Tal vez está de más señalarlo pero recuerda que la ruta que coloques en la función require indica la ubicación del archivo ConsultaXML.php, por lo que si lo almacenas en un directorio distinto al de generaXMLConClase.php deberás indicar ese cambio en el código anterior.

Después del require, creamos una serie de arreglos asociativos que representan el contenido del documento XML definido previamente. En seguida se crea una instancia de la clase ConsultaXML llamada $consultaXML, y como vemos, al utilizar este objeto el código se vuelve bastante pequeño, que es una de las cosas que me gustan de la utilización de objetos: el encapsulamiento que ocurre dentro de los métodos, con el cual se facilita enormemente el trabajo con las clases diseñadas.

Al utilizar el método salida() todo el contenido será regresado al navegador como un archivo XML, y podemos probarlo utilizando un cliente como Internet Explorer o Mozilla Firefox, y mandando llamar al archivo PHP escribiendo una URL como la que sigue en su barra de direcciones:
http://localhost/generaXMLConClase.php

La salida se verá directamente en la pantalla del navegador, como se muestra en la siguiente ejemplo capturado, en el que se utilizó Firefox:



De ésta forma queda lista la refactorización del código para generación de XML del lado del servidor. El siguiente paso será llevar a cabo un cambio más, pero ahora del lado del cliente, para mejorar la forma en la que se acceda a los elementos del documento XML recibido. Sin embargo, ese será el tema de nuestro siguiente post.

Hasta la próxima.