Mostrando entradas con la etiqueta Programación web. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación web. Mostrar todas las entradas

lunes, 14 de junio de 2010

Como devolver arreglos y estructuras de datos en SOAP transparentemente, utilizando PHP y NuSOAP


¡Saludos nuevamente blogueros!

En este artículo veremos como resolver una duda que apareció hace un tiempo, acerca del envío de datos por servicios web utilizando la biblioteca NuSOAP de PHP. En un artículo pasado, hablamos de como implementar un servicio web desde PHP utilizando esta biblioteca; este servicio contaba con dos métodos, uno de ellos devolvía una cadena y el otro un arreglo asociativo de PHP, se pudo consumir el servicio desde un cliente AJAX sin problema procesando el código XML directamente, sin embargo, al trabajar de esta manera se pierde uno de los puntos importantes para utilizar protocolos de comunicación entre aplicaciones: la transparencia. Es decir, al consumir el servicio el cliente no pudo extraer la estructura de datos recibida, sin necesidad de darle un tratamiento al XML de la respuesta y teniendo que saber de antemano cuál era la estructura XML que recibiría.

Ahora, la idea es corregir este problema de transparencia, al mismo tiempo que mostramos como mandar diferentes tipos de datos complejos, como son: estructuras, arreglos y arreglos de estructuras.

Manos a la obra

Para poder llegar a este nivel de transparencia entre aplicaciones es que el protocolo SOAP utiliza el WSDL, el cual además de contener la definición de cada uno de los métodos que posee un servicio web, puede tener también la definición de tipos de datos complejos. Un tipo de dato complejo consiste una colección de valores agrupados dentro de una estructura, y dentro de este tipo de datos es que encontramos los objetos y arreglos.

Podemos definir estos datos complejos utilizando la clase soap_server de la biblioteca NuSOAP. Como recordarán -y si no es así lo menciono- esta clase nos permite crear servicios web en nuestro servidor para que lo consuman los clientes. Dentro de esta clase se encuentra un objeto llamado wsdl, definido como una propiedad de la misma clase, el cual posee una serie de métodos para definir las características del WSDL del servicio web. El método que emplearemos concretamente para definir los nuevos tipos de datos será el addComplexType. El primer paso consistirá en instanciar la clase soap_server de la siguiente forma:

require_once('nusoap.php');
$servidor = new soap_server;


Una vez hecho esto contamos con un objeto de esta clase, para el cual podemos definir sus características:

$ns = "http://nuestroespaciodenombres";
$servidor->configureWSDL('NombreDelServicio',$ns);
$servidor->wsdl->schematargetnamespace = $ns;


Ahora viene lo bueno, definir tipos de datos complejos para que sean devueltos por los métodos de nuestro servicio. Para este ejemplo definiremos 3 estructuras diferentes para probar las posibilidades que nos ofrece esta clase:

  • Primero un arreglo de cadenas sencillo:

$servidor->wsdl->addComplexType(
        'ArregloDeCadenas',
        'complexType',
        'array',
        'sequence',
        'http://schemas.xmlsoap.org/soap/encoding/:Array',
        array(),
        array(array('ref' => 'http://schemas.xmlsoap.org/soap/encoding/:arrayType',
         'wsdl:arrayType' => 'xsd:string[]')
        ),
        'xsd:string'  
);


Como vemos, se incluye una referencia(http://schemas.xmlsoap.org/soap/encoding/:Array) a la definición del formato que debe tener un dato de tipo complejo Array.

  • Después una estructura de datos, similar a los arreglos asociativos, que contendrá una serie de valores distinguidos por un nombre:


  • $servidor->wsdl->addComplexType(
            'Estructura',
            'complexType',
            'struct',
            'all',
            '',
            array(
            'Nombre' => array('name' => 'Nombre', 'type' => 'xsd:string'),
            'Apellidos'=>array('name' => 'Apellidos', 'type' => 'xsd:string'),
            'Edad'=>array('name' => 'Edad', 'type' => 'xsd:integer')
            )
    );


  • Finalmente -y lo más interesante-, un arreglo de estructuras de datos:

$servidor->wsdl->addComplexType(
        'ArregloDeEstructuras',
        'complexType',
        'array',
        'sequence',
        'http://schemas.xmlsoap.org/soap/encoding/:Array',
        array(),
        array(array('ref' => 'http://schemas.xmlsoap.org/soap/encoding/:arrayType',
         'wsdl:arrayType' => 'tns:Estructura[]')
        ),
        'tns:Estructura'  
);


Digo que el último ejemplo me parece el más interesante porque con este podemos definir datos de entrada y de salida para los métodos que consistirán en listas de estructuras de datos, similares a las que encontramos en muchos lenguajes de programación de alto nivel y sobre todo muy útiles para representar registros de bases de datos y archivos estructurados.

Para incluir estos datos de entrada y de salida de los métodos, basta con declararlo en el método registrer donde se declaran los métodos, y asegurarnos por supuesto de que la estructura de los parámetros de estos métodos que hagan referencia los tipos de datos complejos, corresponda con la estructura declarada, es decir:

function consultaPersonas($param) {
    $arreglo = array();
    $arreglo[] = array('Nombre'=>"Juan", 'Apellidos'=>"Torres", 'Edad'=>18);
    $arreglo[] = array('Nombre'=>"Teresa", 'Apellidos'=>"Jiménez Sánchez", 'Edad'=>19);
    $arreglo[] = array('Nombre'=>"Efraín", 'Apellidos'=>"Ovalles López", 'Edad'=>22);
    return $arreglo;
}

$servidor->register('consultaPersonas',
    array('param'=>'xsd:string'),
    array('return'=>'tns:ArregloDeEstructuras'),
    $ns
);



Si por ejemplo creamos un método que devuelva el tipo de dato complejo ArregloDeEstructuras, en el código de nuestro método deberemos incluir una estructura de datos equivalente, en este caso un arreglo asociativo, donde cada llave del arreglo corresponde al identificador de los datos de nuestra estructura previamente definida -llamada Estructura en este ejemplo-; así en el método, cada entrada del arreglo contendrá a la vez un arreglo asociativo, que corresponderá a esta estructura de datos.

De esta forma es posible crear servicios web que devuelvan a nuestras aplicaciones arreglos de estructuras de datos, e incluso arreglos de arreglos, lo cual ayudará a que nuestros servicios puedan generar mayor variedad de datos de salida, y de esta forma aumentaremos el poder las capacidades nuestras aplicaciones. Además de esto, se logra mucha mayor transparencia, ya que cualquier aplicación de terceros o que nosotros mismos desarrollemos, solo tendrá que seguir el formato definido en el WSDL de nuestros servicios web, para poder desentrañar estos tipos de datos complejos. Algunos entornos de desarrollo -como el de .Net- incluso permiten implementar el consumo de estos servicios web interpretando los tipos de datos complejos como objetos nativos del lenguaje, lo que nos permite manipularlos con mucha mayor facilidad, así que las posibilidades son muchas.

Hasta la próxima.

viernes, 11 de junio de 2010

Devolver objetos en JSON desde PHP


Yo digo que es una dona

¡Saludos blogueros!

Ese pequeño artículo va para los que se preguntaban en el ejemplo anterior, ¿qué era ese formato llamado JSON que utiliza flickr para enviarnos la información? Se trata más que nada de una forma para intercambiar datos estructurados entre diferentes sistemas sobre protocolos como HTTP. JSON, define cómo se deberán codificar los objetos de un determinado lenguaje para convertirlos en cadenas de texto, con el propósito de enviarlos hacia otras aplicaciones, y que estas puedan reconstruirlos como objetos nativos, utilizando la decodificación definida por el mismo formato JSON.

Por ejemplo, digamos que mi aplicación desea mandar un objeto que contiene determinada información hacia un cliente Javascript, además de las ya conocidas técnicas basadas en XML o protocolos como SOAP, tenemos la posibilidad de mandarlo codificado en JSON, de modo que desde nuestra aplicación convertiremos ese objeto en una cadena de texto que lo representará, y lo enviaremos a nuestro cliente Javascript como una respuesta en texto simple, el cliente -con previo conocimiento del formato de la respuesta-, solo tendrá que aplicar las reglas de la notación para -partir de la cadena- reconstruir el objeto y utilizarlo dentro de su propia ejecución de instrucciones. Tal y como hicimos en el ejemplo anterior con los servicios de flickr.

La gran ventaja de JSON, es que el cliente Javascript puede utilizar la instrucción eval, para reconstruir el objeto partir de la cadena recibida, sin necesidad de ningún interprete especia, tal y como explica el artículo de la Wikipedia sobre JSON. Del lado de nuestra aplicación en el servidor por otro lado sí se necesitará de una funcionalidad que haga el trabajo de codificar los objetos u arreglos en la notación de JSON, pero para eso la gran mayoría de los lenguajes cuentan ya con bibliotecas de funciones o clases para hacer la tarea.

En este artículo veremos cómo hacer esa tarea del lado del servidor, utilizando la biblioteca de funciones para JSON de PHP, disponible a partir de la versión 5.2.0. Creando la siguiente aplicación web en este lenguaje:

respuestajson.php
<?php

//Clase para representar una serie de diferentes artículos para tiendas

class Articulo {
public $id;
public $nombre;
public $categoria;
public $precioDeSalida;
public $detalles;
}



$articulo = new Articulo();
$articulo->id = 1;
$articulo->nombre = "Nintendo DS";
$articulo->categoria = "Videojuego";
$articulo->precioDeSalida = "$3 500";
$articulo->detalles = array('Soporta juegos para Gameboy advance','Pantalla tactil','Reproducción de MP3');
echo json_encode($articulo);?>


El programa en PHP codificara en notación JSON el objeto $articulo, dando como resultado una cadena que devolverá al cliente, la cual podemos visualizar si accedemos a la aplicación desde el navegador (http://localhost/respuestajson.php):


{"id":1,"nombre":"Nintendo DS","categoria":"Videojuego","precioDeSalida":"$3 500","detalles":["Soporta juegos para Gameboy advance","Pantalla tactil","Reproducci\u00f3n de MP3"]}


Ahora, el cliente AJAX para procesar estos datos sería el siguiente:

clientejson.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Decodifica un objeto en JSON</title>
<script type="application/javascript">
var http_request = new XMLHttpRequest();
var url = "http://localhost/respuestajson.php"; // Esta URL debería devolver datos JSON

// Descarga los datos JSON del servidor.
http_request.onreadystatechange = manejaJSON;
http_request.open("GET", url, true);
http_request.send(null);

function manejaJSON() {
  if (http_request.readyState == 4) {
    if (http_request.status == 200) {
      var cadCodificadaJSON = http_request.responseText;
      var objDatos = eval("(" + cadCodificadaJSON + ")"); //Creamos el objeto utilizando la cadena codificada
      //Ahora con el objeto desplegamos los datos mandados desde el servidor
      document.getElementById("divId").innerHTML = objDatos.id;
      document.getElementById("divNombre").innerHTML = objDatos.nombre;
      document.getElementById("divCategoria").innerHTML = objDatos.categoria;
      document.getElementById("divPrecio").innerHTML = objDatos.precioDeSalida;
      document.getElementById("divDetalles").innerHTML = objDatos.detalles[0] + ", " + objDatos.detalles[1] + ", " + objDatos.detalles[2];
    } else {
      alert("Ocurrio un problema con la URL.");
    }
    http_request = null;
  }
}
</script>
</head>
<body>
<h2>Producto</h2>
<div id="divId"></div>
<div id="divNombre"></div>
<div id="divCategoria"></div>
<div id="divPrecio"></div>
<div id="divDetalles"></div>
</body>
</html>


Este código implementa una sencilla aplicación AJAX, similar a las vistas en los primero ejemplos sobre esta tecnología vistos en este blog. Básicamente accede a la aplicación en PHP que creamos previamente, y procesa el texto plano que recibe como respuesta, almacenándolo en la cadena, debido a que este texto consiste en la codificación en JSON del objeto original, lo decodifica utilizando la instrucción eval("(" + cadCodificadaJSON + ")"), creando el objeto objDatos como resultado.

El resultado obtenido se vería en el navegador como sigue:

Producto


1

Nintendo DS

Videojuego

$3 500

Soporta juegos para Gameboy advance, Pantalla tactil, Reproducción de MP3


De esta forma, podemos transmitir conjuntos de datos estructurados sin recurrir a formatos como XML o protocolos como SOAP, que requieren un mayor trabajo de procesamiento de la información en el cliente, pero que en contra parte, exige que el servidor pueda llevar a cabo la codificación de objetos en JSON. La elección final de la forma en que vayamos a transmitir los datos sobre HTTP, dependerá de los requerimientos que nos exijan la arquitectura y la plataforma que habremos de utilizar.

Saludos.

jueves, 4 de junio de 2009

Notas para iniciarse en PHP, Parte 8 (Final): La funcionalidad


Manos a la obra


Y si ya dominamos todas las bases entonces ¿qué podemos hacer con PHP?

Cómo mencionamos antes, PHP nos ofrece un gran grupo de funciones internas para realizar diferentes trabajos, así como algunas clases complementarias que se han venido incorporando. Entre las funcionalidades cubiertas encontramos el manejo de archivos de texto, manipulación de bases de datos, envío de correos electrónicos, funciones matemáticas, operaciones con cadenas de texto, uso de los puertos físicos del equipo, manejo de archivos XML, compresión de archivos, creación de imágenes, y eso no es todo, al revisar el manual encontramos muchas opciones más, ya que las funcionalidades que no se encuentran dentro de PHP se pueden incorporar por medio de extensiones, lo cual se ha venido haciendo con varias de las funciones antes mencionadas, como es el caso de el manejo de las bases de datos, gracias a lo que ahora PHP cuenta con una colección de APIs que le permiten acceder a las gran mayoría de los sistemas de bases de datos existente, incluyendo los más importantes de la actualidad como son:
Junto con un grupo de funciones para una capa de abstracción de datos, que nos permiten acceder a diferentes fuentes de datos desde una misma interface:
  • DBA — Database (dbm-style) Abstraction Layer
  • dbx
  • ODBC — ODBC (Unified)
  • PDO — PHP Data Objects
Aquellas extensiones que no estén incorporadas dentro de las opciones del PHP con el que estemos trabajando, pueden incluir fácilmente de diferentes maneras. En Windows basta con copiar los archivos binarios (DLL) en el directorio de extensiones de PHP, normalmente ubicado en el directorio /ext dentro del directorio principal de PHP, y modificar el archivo de configuración php.ini agregando en la sección de extensiones dinámicas la línea extension=nombre_de_la_extension.dll, por ejemplo, para agregar la extensión de acceso a bases de datos de MS SQLServer, bastaría con agregar extension=msql.dll, entonces solo tendríamos que reiniciar el servidor Web para que el cambio surtiera efecto.

En GNU/Linux en cambio, en lugar de archivos DLL se utilizan los archivos Shared Object o de objetos compartidos, que funcionan de manera similar a los DLL ya que nos permiten agregar funcionalidades adicionales a nuestras aplicaciones desde un componente independiente. Para el ejemplo anterior, primero tendríamos que haber incorporado a PHP el archivo binario .so de la extensión, y después incluir en el archivo de configuración la línea extension=msql.so, el primer paso se puede hacer de diferentes formas, como con los asistentes de actualización de software que nos brinda la distribución del sistema operativo que estemos utilizando, algunos de los cuales nos liberan del segundo paso de modificar el archivo php.ini, otra opción más es utilizar el módulo php-dev, que desde la consola de comandos nos permite compilar e instalar extensiones dinámicas para PHP sin la necesidad de tocar el archivo de configuración, gracias a que incorpora a nuestro sistema la instrucción phpize.


Algunas funciones interesantes

Ahora mostraré algunos ejemplos de las diferentes funciones trabajando, como una muestra de los que se puede hacer con PHP, veamos:

Manejo de archivos de texto

Entre las funciones para manipulación de archivos de texto, encontramos las funciones fread() y fwrite(), que nos permiten leer y escribir en archivos respectivamente. En el siguiente ejemplo se muestra una de los usos que podemos darle a estas funciones implementado un contador de vistas:

contador.php
<?php
$total = 0;
$archivo = "contador.txt"; //Nombre del archivo de texto
if(!file_exists($archivo)) { //Comprobamos la existencia del archivo contador.txt
$abre = fopen($archivo, "w"); //De no existir el archivo
$grabar = fwrite($abre, "0"); //lo creamos guardando el valor de 0 dentro
fclose($abre);
}
else {
$grabar = true;
}
if($grabar) {
// Abrimos el archivo para solamente leerlo ("r" para "read")
$abre = fopen($archivo, "r");
// Leemos todo el contenido del archivo
$total = fread($abre, filesize($archivo));
// Cerramos el archivo
fclose($abre);

// Abrimos nuevamente el archivo pero ahora para escribir
$abre = fopen($archivo, "w");
// Sumamos 1 nueva visita
$total = $total + 1;
// Y reemplazamos el contenido por la nueva cantidad de visitas
$grabar = fwrite($abre, $total);
// Cerramos la conexión al archivo
fclose($abre);
}
?>
<html>
<head><title>Contador de visitas</title></head>
<body>
<div align="center">
Eres el visitante número <?php echo $total?> de esta página.
</div>
</body>
</html>


El programa PHP implementa nuestro contador, el cual va llevando dentro de un archivo de texto la cuenta de las ocasiones en que la página es cargada por los usuarios, para posteriormente desplegar la información dentro de la misma.

Si lo que queremos en cambio es acceder únicamente al texto de un archivo para usarlo como una cadena, es más eficiente utilizar la función file_get_contents(), que recibe el nombre o la URL de un archivo y devuelve su contendido como una cadena de texto para que la podamos manipular.

Funciones para cadenas de texto

Entre la gran cantidad de funciones existentes para el manejo de cadenas de texto, encontramos un interesante conjunto contenido en por las funciones POSIX Regex y PCRE, ya que en ambos grupos encontramos que podemos utilizar expresiones regulares para el trabajo de cadenas, así como para la transformación de arreglos a cadenas y viceversa.

Una muestra interesante de esto es la función split que encontramos entre las funciones POSIX:
<?php
$cadena = "En ese camino lago\nQué un día me vio caminar\nNació esta ciega herida";
$arreglo = split("\n", $cadena); //Separamos la cadena por los saltos de línea que contenga
print_r($arreglo);
?>


La cual permite crear un arreglo a partir de una cadena de texto, dividiendo en partes la cadena según el texto que proporcionamos como segundo parámetro a la función. El ejemplo anterior arrojaría un resultado como el siguiente en el navegador:
Array ( [0] => En ese camino lago [1] => Qué un día me vio caminar [2] => Nació esta ciega herida )


Las funciones PCRE son extensiones tipo PERL, que utilizan en todo momento expresiones regulares para trabajar, las cuales resulta muy útiles al momento de validar cadenas de texto, por ejemplo si queremos saber si una cadena cumple con un determinado formato podemos evaluarla con la función preg_match:

validarCadena.php
<?php
$er_nombre = "/^([a-zA-Z]|[ÁÉÍÓÚáéíóúÑñÜü]|[\s,\.'\-])+$/"; //Expresión regular que será utilizada
if(isset($_GET['txtNombre'])) {
$nombre = stripslashes($_GET['txtNombre']); //Eliminamos el caracter de diagonal invertida que PHP agrega automaticamente para los caracteres de comilla simple (')
if(preg_match($er_nombre,$nombre)) //Evaluamos el contenido del texto recibido
$respuesta = "Nombre correcto :)";
else
$respuesta = "Nombre no válido :(";
}
?>
<html>
<head><title>Validador</title></head>
<body>
<form method="get">
Escribe un nombre <input type="text" name="txtNombre" /> <input type="submit" name="enviar" value="Validar" />
</form>
<p><?php echo $respuesta?></p>
</body>
</html>


En el ejemplo anterior se creó una expresión contenida en $er_nombre que sirvió para validar el texto capturado en el formulario de la página, al ser utilizada por la función preg_match(), la cual devuelve un valor booleano dependiendo de si el texto cumple o no con la expresión regular. Puede verse entonces que estas expresiones son herramientas muy útiles al momento de validar los datos capturados por los usuarios en nuestras páginas.

Sin embargo es recomendable utilizar las expresiones regulares solo cuando la situación lo amerita, ya que el tiempo de procesamiento que consumen es superior al de otras instrucciones de procesamiento de texto, que cuales utilizan cadenas simples como parámetros para manipular cadenas.

Manipulación de bases de datos

Como un ejemplo de la funcionalidad ofrecida, vamos probar la extensión mysql par el manejo de bases de datos MySQL, con la cual ya trabajamos en algunos de los artículos antiguos, convendría que revisaran el tema de Introducción al uso de MySQL que tratamos en este blog hace un tiempo para poder seguir este ejemplo, ya que aquí trabajaremos con la misma base de datos. En el siguiente código podemos ver el programa PHP en acción:

consultaMySQL.php
<?php
//Parámetros para la conexión a la base de datos
$servidor = "localhost";
$usuario = "root";
$contrasenia = "[TuConstraseña]";
$baseDeDatos = "obras_literarias";
$conexion = @mysql_connect($servidor,$usuario,$contrasenia); //Abrimos la conexión con MySQL
if(mysql_select_db($baseDeDatos,$conexion)) { //Accedemos a la base de datos
//Establecemos que la información que se intermabiará estará codificada en utf-8
mysql_query("SET NAMES 'utf8'",$conexion);
//Comprobamos que se recibieron correctamente
if(isset($_GET['txtNombre']) && ($nombre = stripslashes(trim($_GET['txtNombre'])))!="") {
$consulta = "SELECT * FROM autores WHERE Nombre LIKE '%".addslashes($nombre)."%'";
$resultado = mysql_query($consulta,$conexion); //Realizamos la consulta
if($resultado) { //Si no hubo error
if(mysql_num_rows($resultado) > 0){//Comprobamos si se encontraron registros
//Almacenamos en una matriz de arreglos asociativos los registros que resultaron
$matriz = array();
while($renglon = mysql_fetch_assoc($resultado)) {//Extrae un renglon del resultado como un arreglo asosciativo
$matriz[] = $renglon;
}
}
else{
$mensajeDeError = "Autor no encontrado";
}
}
else {
$mensajeDeError = mysql_error();
}
}
else {
$mensajeDeError = "Datos imcompletos";
}
}
else{
$mensajeDeError = mysql_error();
}
?>
<head>
<title>Consulta a MySQL</title>
</head>
<body>
<form method="get">
Escribe un nombre <input type="text" name="txtNombre" /> <input type="submit" name="enviar" value="Validar" />
</form>
<? if($mensajeDeError != "") :?>
<p><?php echo $mensajeDeError?></p>
<? else :?>
<table>
<tr><td>Id</td><td>Nombre</td><td>Nacionalidad</td><td>Profesion</td></tr>
<? foreach($matriz as $autor):?>
<tr><td><?=$autor['Id']?></td><td><?=$autor['Nombre']?></td><td><?=$autor['Nacionalidad']?></td><td><?=$autor['Profesion']?></td></tr>
<? endforeach;?>
</table>
<? endif;?>
</body>
</html>

*Nota: Si desean probar el programa recuerden reemplazar el valor de la contraseña en el texto señalado por [tuContraseña].

En este ejemplo, vemos la forma en la que podemos acceder a una base de datos de MySQL y realizar una consulta sobre una tabla, para posteriormente desplegar la información obtenida dentro de una página HTML, lo cual puede parecer algo muy sencillo pero nos permite darnos una idea del potencial de PHP para trabajar son sistemas manejadores de bases de datos. Para ampliar un poco más el ejemplo anteriorla es también pueden revisar el post de este blog titulado Otra forma de acceder a la base de datos con PHP, donde se describe la creación de una clase para abstracción del acceso a datos, es decir que creamos una clase que encapsulará todas las funcionalidades de la extensión mysql dentro de sus métodos, para facilitar el trabajo con la base de datos y el mantenimiento de nuestros programas.

Espero que con este breve artículo hayan podido hacerse una idea general de la funcionalidad de PHP para la creación de proyectos profesionales, como siempre hay que recordar que estos documentos son un compendio de los contenidos de PHP pero no una guía definitiva, por lo que de estarse iniciando en el aprendizaje del lenguaje conviene que revisen los enlaces que fuí colocando como referencias en cada tema. Ahora dependerá de cada desarrollador decidir si PHP es la herramienta que necesitan para resolver sus problemas y de ser así, qué funciones del lenguajes les serán útiles. Y con esta conclusión es que finaliza la serie de post sobre el aprendizaje de PHP que he venido realizando, espero que más de un programador encuentre útil alguna de mis notas y de ser posible me envíen algún comentario.

Hasta la próxima.

martes, 2 de junio de 2009

Notas para iniciarse en PHP, Parte 7: Manejo de variables predefinidas


Vamos a interactuar con nuestro usuarios, por supuesto a través del navegador


¡Saludos nuevamente!

Como mencionamos en el tema de conceptos básicos, PHP posee un conjunto de variables que nos permiten obtener información de distintas fuentes como: los datos de formularios, las cookies, las sesiones, el servidor, entre otras cosas. Estas son llamadas variables predefinidas y trataremos con ellas en este artículo.

Leer datos de formularios

Los formularios HTML permiten a los usuarios capturar datos en diferentes formatos, y la información capturada puede ser enviada al servidor al momento que los usuarios lo ordenen, lo más común es que se incluya un input de tipo submit para realizar esta tarea, de modo que cuando el usuario realice el submit el formulario enviará la información contenida siguiendo las especificaciones que nosotros mismos le definimos:
<html>
<head>
<title>Formulario con GET</title>
</head>
<body>
<form action="leerDatos.php" method="get" enctype="text/plain">
Nombre: <input type="text" name="nombre" size="25" maxlength="50"><br />
Apellidos: <input type="text" name="apellidos" size="35" maxlength="100"><br />
Sexo: <select name="sexo">
<option value="M">Masculino</option>
<option value="F">Femenino</option>
</select><br />
<input type="submit" name="envio" value="Enviar" />
</form>
</body>
</html>


*Nota: si no se define el atributo name de una entrada del formulario este dato no será enviado con el resto de la información.

En el ejemplo vemos que se definió un formulario con una serie de entradas para captura y un submit para enviar la información, las especificaciones del envío se establecen como atributos del elemento <form>, de modo que definimos que el método de envío será GET, y la página a la que serán enviados es leerDatos.php, cuyo código aparece a continuación:

leerDatos.php
<?php
if(isset($_GET['envio']))
{
$nombre = $_GET['nombre'];
$apellidos = $_GET['apellidos'];
$sexo = ($_GET['sexo'] == "M")? "Masculino":"Femenino";
echo "Usted ha enviado al servidor el nombre $nombre, $apellidos, con el sexo $sexo";
}
?>


Los datos pueden ser enviados al servidor por dos tipos de métodos: GET o POST, lo cual se especifica en el atributo method del formulario; en el método GET los datos que se se envían dentro de la URL de la página destino, es decir la página Web que recibe los datos, como ocurrió con el ejemplo anterior. En el método POST en cambio, los datos no podrán ser visualizados por los usuarios después de que fueron enviados a la página destino, pero de forma similar a la del ejemplo anterior pueden ser leídos por nuestra aplicaciones Web.

En el caso del método GET en nuestra aplicación en PHP utilizamos la variable predefinida $_GET para leer los datos recibidos. Esta variable se lee como si se tratara de un arreglo asociativo, donde cada llave corresponde al nombre (es decir, atributo name) de un campo, así la forma de acceder al valor del campo de texto "nombre" es por medio de la variable $_GET['nombre'] por ejemplo.

Si deseamos probar el método POST bastará con que en el formulario cambiemos el atributo method por "post", y que en nuestro programa leerDatos.php se accese a los datos por medio de la variable predefinida $_POST como se muestra a continuación:

leerDatos.php
<?php
if(isset($_POST['envio']))
{
$nombre = $_POST['nombre'];
$apellidos = $_POST['apellidos'];
$sexo = ($_POST['sexo'] == "M")? "Masculino":"Femenino";
echo "Usted ha enviado al servidor el nombre $nombre, $apellidos, con el sexo $sexo";
}
?>


El método POST es una buena forma de mantener ocultos los datos capturados por el usuario en nuestros formularios, como pueden ser contraseñas e información que será almacenada en un archivo o en una base de datos. En cambio el método GET se recomienda cuando no existe problema con que la información enviada sea visualizada en el navegador del usuario, como por ejemplo en las búsquedas que hacemos por Internet. Ésta última forma también resulta muy útil cuando en nuestro navegador queremos regresar a una página anterior, en la que se realizó un submit por GET, ya que los navegadores conservan las direcciones URL de todas las páginas desplegadas, incluyendo los datos enviados por GET, de modo que la lectura de datos enviados se puede volver a realizar como la primera vez. Además el método GET permite enviar ciertos datos a una página sin necesidad de un formulario, si dentro de nuestra dirección URL incluimos los datos por nosotros mismos, por ejemplo si en la barra de direcciones del navegador escribimos la siguiente URL:

http://localhost/leerDatos.php?nombre=Beto&apellidos=Gonzalez&envio=Enviar


El resultado será el mismo a si hubiéramos utilizado la página HTML con su formulario.

*Nota: Las direcciones incluidas en en el atributo method de los formularios pueden incluir cualquier tipo de dirección URL.

Además de las dos formas anteriores, PHP también incluye la variable predefinida $_REQUEST, la cual puede recoger los datos enviados por ambos métodos, así como los datos de $_COOKIE de la que se hablará más adelante.

Envio de archivos

Dentro de las entradas de un formulario se incluye el tipo file, el cual nos permite capturar un archivo dentro de nuestro equipo y enviarlo al servidor Web:
<html>
<head>
<title>Formulario con envío de archivos</title>
</head>
<body>
<form action="leerArchivo.php" method="post" enctype="multipart/form-data">
Nombre: <input type="text" name="nombre" size="25" maxlength="50"><br />
Apellidos: <input type="text" name="apellidos" size="35" maxlength="100"><br />
Sexo: <select name="sexo">
<option value="M">Masculino</option>
<option value="F">Femenino</option>
</select><br />
Archivo: <input type="file" name="imagen" /><br />
<input type="submit" name="envio" value="Enviar" />
</form>
</body>
</html>


Es indispensable para enviar un archivo que el enctype de nuestro formulario este definido como "multipart/form-data" y que el método de envío sea POST.

Para acceder al archivo del usuario, desde nuestra aplicación Web utilizaremos la variable predefinida $_FILES, en nuestro ejemplo el programa se almacenaría dentro del archivo leerArchivo.php, que es al que se envía la información del formulario:

leerArchivo.php
<?php
if(isset($_POST['envio'])) {
$nombre = $_POST['nombre'];
$apellidos = $_POST['apellidos'];
$sexo = ($_POST['sexo'] == "M")? "Masculino":"Femenino";
echo "Usted ha enviado al servidor el nombre $nombre, $apellidos, con el sexo $sexo.";
if(isset($_FILES['imagen'])) {//Comprobamos que se recibió el archivo
$nomArchivo = $_FILES['imagen']['name']; //Nombre del archivo
$tipArchivo = $_FILES['imagen']['type']; //Tipo de archivo
$tmpArchivo = $_FILES['imagen']['tmp_name']; //Nombre temporal del archivo (con el que se almacenó en el servidor)
$tamArchivo = $_FILES['imagen']['size']; //Tamaño del archivo
$errArchivo = $_FILES['imagen']['error']; //Mensaje de error generado, si es que hubo alguno
if($errArchivo == "") { //Si no hubo error
echo "<br />El archivo de nombre $nomArchivo fué recibido correctamente, cuenta con un tamaño de $tamArchivo bytes y es del tipo $tipArchivo, almacenándose en el servidor con el nombre temporal de $tmpArchivo.";
}
else {
echo "<br />Error de recepción de archivo: $errArchivo";
}
}
}
?>


Como vemos, la variable $_FILES tiene un primer índice para localizar el nombre el archivo enviado utilizando el name de la entrada file del formulario, después utiliza un segundo índice para obtener la información del archivo recibido, entre los que se encuentran el nombre, tamaño y tipo de archivo recibido, que utilizamos en el ejemplo para desplegar como información del archivo recibido. Además se incluye un campo dentro del arreglo para el manejo de errores. Si quisiéramos acceder al contenido del archivo recibido tendríamos que utilizar el campo $_FILES['imagen']['tmp_name'], que guarda el nombre temporal del archivo, es decir el nombre con el que el archivo se almacenó en nuestro servidor -normalmente en un directorio de archivos temporales-, para con esa dirección física poder leer su contenido o copiarlo a otra dirección, o realizar la labor que quisiéramos hacer con el.

Es importante notar que pueden existir algunos errores asociados con el envío de archivos al servidor, el más común consiste en que el tamaño del archivo enviado supera las especificaciones del archivo de configuración php.ini.

Manejo de Cookies

Las cookies son archivos de texto que almacenan nuestros navegadores, dentro de los que registran información relacionada con nuestra una visita a alguna página, para de esta forma poder recuperar estos datos en un acceso posterior que hagamos, de modo que el usuario no necesite volver a capturarlos. Un ejemplo de su uso lo encontramos en los sitios que proporcionan la opción de almacenar nuestro nombre de usuario y contraseña después de iniciar sesión, para que en nuestros siguientes ingresos no tengamos que volver a teclearlos.

PHP nos permite grabar y leer cookies utilizando la función setcookie() y la variable predefinida $_COOKIE, respectivamente.

Una cookie consiste en una variable que registramos por medio de la función anterior, dentro de la cual definimos su nombre y valor, una vez hecho esto podemos leer el valor de la variable por medio de la variable predefinida $_COOKIE, la cual también se accede como un arreglo asociativo -de manera similar a las otras variables que hemos visto-, lo interesante aquí es que al refrescar la página o cambiarnos a cualquier otra página, la información se conservará, ya que nuestro navegador conservará la información que declaramos con setcookie dentro de un archivo. Por ejemplo:

cookie.php
<?php
$nombre = "unaVariable";
$valor = "Hola mundo!";
if(!isset($_COOKIE['unaVariable'])) {
setcookie($nombre, $valor);
echo "Cookie establecida";
}

else {
echo "El valor de la cookie es ".$_COOKIE['unaVariable'];
}
?>


Si probamos el ejemplo anterior, tendremos al inicio el mensaje "Cookie establecida", debido a que al ingresar a la página por primera vez esta variable aún no había sido registrada y se tuvo que realizar este trabajo, si refrescamos la página entonces nos encontraremos con que el mensaje desplegado es "El valor de la cookie es Hola mundo!", ya que la cookie identificada con $_COOKIE['unaVariable'] ya fue almacenada por el navegador. Podemos refrescar la página y cambiarnos de ubicación tantas veces como queramos pero al volver a ejecutar el programa el resultado será siempre el mismo. Las cookies pueden ser eliminadas directamente desde el navegador del archivo, o si durante su establecimiento incluimos en la función setcookie un tiempo de expiración para la éstas.

Además de datos simples también se pueden almacenar datos compuestos como arreglos y objetos utilizando las funciones serialize() y unserialize(), serializando el contenido de uno de estos dos tipos de datos como una cadena y registrándola con un dato simple, para reconstruir el arreglo u objeto original aplicamos la desserialización que proporciona la función unserialize().

Utilización de sesiones

PHP puede crear sesiones que nos permiten mantener ciertos datos en la memoria del navegador, de forma que podamos acceder a ellos en cada una de las páginas que visitemos mientras el navegador se encuentre en ejecución. Así, nuestras aplicaciones en PHP pueden contar con información importante, sin tener que solicitarla constantemente al usuario o tener que acceder a un medio externo como un archivo o una base de datos, haciéndolas de esta manera más dinámicas. Los datos pueden ser eliminados al cerrar el navegador o utilizar una función explícita del lenguaje para ese trabajo.

Estos datos se escriben en la sesión por medio de la variable predefinida $_SESSION, para crear una variable dentro de este arreglo asociativo antes tenemos que iniciar una sesión utilizando la función session_start(), para posteriormente declarar las variables de sesión que vayamos a manejar, como se muestra en el siguiente ejemplo:

session1.php
<?php
session_start();
$valor = "Hola mundo!";
if(!isset($_SESSION['unDato'])) {
$_SESSION['unDato'] = $valor;
echo "Variable de sesión establecida";
}
else {
echo "El valor de la variable es ".$_SESSION['unDato'];
}
?>


En el ejemplo se inicia una sesión con la función sesion_start() para porteriomente comprobar si ya existe la variable de sesión $_SESSION['unDato'], en caso de no ser así se crea esa variable, para que en un siguiente acceso a la página veamos que el valor se mantuvo en la sesión. Si queremos que en cada página podamos acceder a un dato almacenado en la sesión es necesario incluir la función sesion_start() al inicio de éstas. Esto nos permitirá conservar valores al cambiar de páginas, si por ejemplo tenemos una segunda página:

session2.php
<?php
session_start();
if(isset($_SESSION['unDato'])) {
echo "El valor de la variable sigue siendo ".$_SESSION['unDato'];
}
else
echo "Variable no declarada";
?>


El valor original se conservará. Una idea común es que los usuarios realicen la captura los datos que requeriremos en varias páginas una sola vez, y estos los mantengamos con las variables de sesión.

Esta variables pueden ser eliminadas de la memoria del navegador por medio de las funciones session_unset() y session_destroy(), una seguida de la otra ya que la primera elimina todas las variables de sesión y la segunda destruye la sesión actual, o también se pueden eliminar variables específicas por medio de la función unset(), si le pasamos como parámetro el nombre de la variable de sesión.

Así como con las cookies, podemos escribir datos compuestos en las sesiones utilizando las funciones serialize() y unserialize().

Dentro del manual oficial podemos encontrar el grupo completo de funciones relacionadas con las sesiones.

Datos del servidor

También podemos obtener cierta información relacionada con el servidor Web en el que ejecutamos nuestros programas PHP a través de la variable predefinida $_SERVER, como por ejemplo, la ubicación del archivo que se está ejecutando, la hora del servidor, su sistema operativo, entre otras cosas. En el siguiente programa se muestra como podemos desplegar la información del servidor en nuestro navegador:

server1.php
<?php
echo "Cliente del usuario (navegador Web): ".$_SERVER['HTTP_USER_AGENT']."<br />";
echo "Software del servidor: ".$_SERVER['SERVER_SOFTWARE']."<br />";
echo "Archivo PHP que estamos ejecutando: ".$_SERVER['PHP_SELF']."<br />";
?>


Variables de entorno

También existe la variable predefinida $_ENV, la cual nos permite acceder a la información del entorno en el cual se están ejecutando nuestros programas PHP, como por ejemplo en nombre del usuario que ejecuta el script en el servidor o el nombre del servidor.

Y con estas últimas anotaciones es que concluimos con el artículo sobre variables predefinidas, ahora ya pueden hacerse una idea de la forma en la que sus aplicaciones pueden interactuar con sus usuarios finales, para construir aplicaciones completas, para lo que solo falta que conozcan las diferentes funcionalidades que pueden poseer nuestros programas gracias a las muchas opciones que PHP posee.

Hasta la próxima.

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.