Descargue como TXT, PDF, TXT o lea en línea desde Scribd
Descargar como txt, pdf o txt
Está en la página 1de 187
Programador PHP de Eugenia Bahit se distribuye bajo una Licencia Creative Commons
Atribuci�n-NoComercial-SinDerivadas 3.0 Unported.
Comparte el conocimiento Eres libre de: � Copiar, distribuir y compartir este libro Bajo las siguientes condiciones: � Reconocer y respetar la autor�a de la obra � No hacer uso comercial de ella � No alterar el contenido �2010-2012 Eugenia Bahit Buenos Aires, Argentina Programador PHP Experto Eugenia Bahit �ndice General Introducci�n a las tecnolog�as GLAMP........................19 GLAMP y LAMP..............................................................................1 9 Diferencia entre GNU/Linux y Linux..............................................19 Free Software Foundation y el Proyecto GNU................................20 Informaci�n complementaria........................................................20 Sitios Web de Referencia...........................................................20 Bibliograf�a complementaria.....................................................20 Preparaci�n b�sica del entorno de desarrollo...............................21 Instalaci�n de Ubuntu GNU/Linux en Windows..........................21 Instalaci�n de Ubuntu GNU/Linux como �nico Sistema Operativo ................................................................................... ...............22 Instalaci�n de Apache Server....................................................22 Otras opciones de Apache:...................................................25 It Works!............................................................................. ...26 Instalaci�n de MySQL................................................................27 Instalaci�n de PHP....................................................................27 Verificando la versi�n de PHP................................................27 Configurando el directorio de tu Web Local..................................28 Crear el directorio para tu Web local.........................................28 Modificando el directorio ra�z de tu Web local...........................29 Conociendo PHP........................................................31 Etiquetas de apertura y cierre .....................................................31 Conocer el valor actual de short_open_tag...............................33 Instrucciones y estructuras...........................................................34 Identaci�n en PHP.........................................................................34 Impresi�n en pantalla...................................................................35 Variables y tipos de datos b�sicos................................................37 3 Programador PHP Experto Eugenia Bahit Null, var_dump() e isset()..........................................................40 Malas pr�cticas con variables, que afectan el uso de memoria 43 Operadores aritm�ticos.................................................................44 Haciendo c�lculos con el IVA.....................................................44 settype �un bug no resuelto o feature objetable?.....................45 HTML y PHP en un mismo archivo.................................................47 Una mala pr�ctica: colocar c�digo HTML embebido dentro de variables de PHP.......................................................................47 Una buena pr�tica para evitar lo anterior.................................48 Comentando y documentando el c�digo fuente...........................49 Inclusi�n de archivos en PHP........................................................51 Diferencia entre inclusi�n de archivos remotos y locales..........52 Diferencia entre include y require.............................................53 Include y require "_once"..........................................................53 Estructuras de Control � Parte I (condicionales)..........54 Definici�n......................................................................... .............54 Condicionales if, else y else if.......................................................54 Operadores l�gicos y de comparaci�n......................................55 Operadores de comparaci�n.....................................................55 Diferencia entre igualdad e id�ntico en la comparaci�n...........56 Operadores l�gicos...................................................................56 Creando condicionales..............................................................57 Estructuras de control con Switch.................................................59 �Cu�ndo utilizar if y cuando switch?.........................................61 Tipos de Datos complejos: Matrices simples y multidimensionales...................................................62 Matrices en PHP............................................................................62 Sintaxis b�sica..........................................................................62 Imprimir en pantalla con print_r................................................63 4 Programador PHP Experto Eugenia Bahit Acceso a los �tems de un array.................................................63 Modificando elementos.............................................................64 Agregar elementos....................................................................64 Estructuras de Control � Parte II (bucles I)..................66 Recorriendo matrices din�micamente con foreach.......................66 Sintaxis b�sica del constructor foreach....................................66 Un ejemplo de iteraci�n compleja con foreach.........................67 Modificando matrices din�micamente con foreach...................68 Estructuras de Control � Parte III (bucles II)................71 While, un bucle simple..................................................................71 Un ejemplo sencillo...................................................................71 Un ejemplo pr�ctico..................................................................72 Do while, tan simple como while pero con una ventaja................73 Un ejemplo simple....................................................................73 Bucles for, los m�s complejos de PHP...........................................75 Sintaxis:.......................................................................... ..........75 Curiosidades sint�cticas de la bipolaridad no diagnosticada de PHP ................................................................................... ...................78 Goto, si lo usas... es tu elecci�n!..............................................79 Un ejemplo no-pr�ctico para entender goto.............................79 Funciones definidas por el usuario.............................82 Definici�n......................................................................... .............82 Declarando Funciones...................................................................82 Sintaxis b�sica..........................................................................82 Sobre el nombre de las funciones.............................................82 Sobre los par�metros................................................................82 Llamando a una funci�n................................................................83 Sobre la finalidad de las funciones................................................84 5 Programador PHP Experto Eugenia Bahit Paso de variables por referencia en funciones..............................84 Modificando variables globales mediante el uso de global...........85 Llamadas de retorno.....................................................................86 Pasar argumentos en una llamada de retorno..........................87 Argumentos no conocidos.........................................................88 Conocer la cantidad de argumentos.....................................88 Obtener una lista completa de todos los argumentos...........89 Obtener un argumento espec�fico.........................................89 Saber si una funci�n puede ser llamada (callable)....................89 Material de lectura adicional.....................................................90 Diferentes formas de recoger argumentos para hacer una llamada de retorno....................................................................91 Forma 1: recibir argumentos en un array..............................91 Forma 2: recibir argumentos 1 a 1........................................92 Llamadas recursivas.....................................................................93 Helpers............................................................................ ..............95 Un helper que retorna la fecha actual.......................................95 Un helper que modifica una variable global, haciendo una llamada de retorno....................................................................96 Taller de Funciones........................................................................97 Trabajando con el Sistema de Archivos.......................98 Recorrido r�pido por las principales funciones..............................98 Apertura de archivos.................................................................98 Modos de apertura................................................................99 Ruta hacia el archivo...........................................................100 Utilizar o no include_path....................................................100 Lectura de Archivos................................................................100 Escribir en un archivo.............................................................101 Moviendo el puntero dentro del archivo..................................102 6 Programador PHP Experto Eugenia Bahit Un contador de visitas sencillo...........................................102 �Cuidado con los permisos!.....................................................103 Trabajando con directorios..........................................................103 Creando el gestor....................................................................103 Explorando el contenido de un directorio...............................104 Filtrando el tipo de elemento..................................................106 Otras funciones que necesitar�s con frecuencia.........................108 Comprobar la existencia de un archivo o directorio................108 Comprobar si un archivo o directorio es legible......................109 Comprobar si un archivo o directorio puede escribirse...........110 M�s funciones sobre el sistema de archivos...........................110 Procesamiento de texto y manipulaci�n de strings....110 Ampliando la definici�n de variables de tipo string....................111 Escapando caracteres.................................................................112 Caracteres de escape.............................................................112 Funciones para manipulaci�n de strings.....................................114 Funciones de escape...............................................................114 Funciones de conversi�n.........................................................115 Evitando ejecuci�n de c�digo no deseado..........................116 Funciones de formato..............................................................116 Funciones de manipulaci�n.....................................................120 Manipulando subcadenas en cadenas................................123 Funciones de encriptaci�n......................................................125 Resumen de las principales funciones de string.........................127 Taller de Archivos y Procesamiento de Formularios.....................129 Constantes, variables variables y variables superglobales.........................................................130 Constantes......................................................................... .........130 7 Programador PHP Experto Eugenia Bahit Definici�n cl�sica de constantes en PHP.................................130 Definici�n de constantes en PHP 5.3.......................................131 Finalidad de las constantes.....................................................132 Variables variables......................................................................133 Variables superglobales..............................................................134 Env�o de correo electr�nico con PHP.........................136 La funci�n mail() y su sintaxis.....................................................136 El par�metro �destinatario�: formatos admitidos....................136 Cabeceras adicionales como par�metro extra........................137 Comprobando que el e-mail pudo enviarse.............................137 Enviando mensajes en formato HTML.........................................139 Funciones para el manejo de Fecha y Hora................140 Funciones simples de fecha y hora.............................................140 Obtener la fecha y hora actual en un array asociativo...........140 Obtener fecha y hora actual con formato en una cadena de texto.............................................................................. .........141 Validar una fecha....................................................................144 C�lculo de fecha / hora sencillo..............................................144 Ejemplos pr�cticos de c�lculos basados en fechas.....................146 �Cu�nto tiempo ha pasado?....................................................146 �Qu� edad tiene...?.................................................................147 �En qu� fecha naci�...?...........................................................147 Funciones matem�ticas...........................................149 Obtener un n�mero elevado a la potencia..................................149 Obtener el n�mero m�s alto y el n�mero m�s bajo....................149 Redondear un n�mero con N cantidad de decimales..................150 Redondear un n�mero hacia abajo.............................................150 Redondear un n�mero hacia arriba.............................................150 8 Programador PHP Experto Eugenia Bahit Obtener un n�mero entero aleatorio..........................................151 Funciones para el manejo de matrices......................151 Diviendo y uniendo arrays..........................................................151 Dividir un array en matrices m�s peque�as...........................151 Obtener la porci�n espec�fica de un array..............................153 Combinar dos arrays, utilizando uno para las claves y otro para los valores............................................................................ ...153 Combinar dos o m�s arrays....................................................154 Combinar dos o m�s arrays multidimensionales de manera recursiva.......................................................................... .......154 Ordenando Arrays por sus valores..............................................155 Ordenar un array de menor a mayor......................................155 Ordenar un array de mayor a menor......................................155 Ordenar un array de menor a mayor manteniendo la relaci�n con los �ndices........................................................................156 Ordenar un array de mayor a menor manteniendo la relaci�n con los �ndices........................................................................156 Ordenando Arrays por su clave...................................................156 Ordenar un array de menor a mayor por su clave..................156 Ordenar un array de mayor a menor por su clave..................157 Comparando funciones de ordenamiento de arrays...................157 Agregar y Eliminar elementos de un array..................................158 Agregar elementos al final del array.......................................158 Agregar elementos al comienzo del array...............................158 Eliminar el �ltimo elemento de un array.................................159 Eliminar el primer elemento de un array................................159 Eliminar valores duplicados en un array.................................159 B�squedas y filtros......................................................................160 Contar la cantidad de veces que los elementos aparecen en un array.............................................................................. .........160 9 Programador PHP Experto Eugenia Bahit Contar la cantidad de elementos de un array.........................160 Obtener la suma matem�tica de los valores de un array.......161 Obtener las diferencias entre dos o m�s arrays......................161 Filtrar datos de un array, utilizando una funci�n de retorno. . .162 Verificar si un array contiene una clave determinada.............163 Obtener todas las claves de un array o todos los valores.......163 Verificar si un array contiene una valor determinada.............164 Buscar un valor detrminado en un array y obtener su clave correspondiente.................................................................... ..164 Cookies y Sesiones de usuario.................................165 �Qu� es una cookie?...................................................................165 Las cookies no son eternas.....................................................166 �Qu� son las sesiones de usuario?..............................................166 Usos e importancia.....................................................................167 Lo b�sico............................................................................. ........167 Creaci�n, lectura, modificaci�n y eliminaci�n de cookies.......167 Crear una cookie.................................................................167 Leer una cookie...................................................................169 Modificar una cookie...........................................................169 Eliminar una cookie.............................................................169 Un ejemplo pr�ctico con Cookies............................................170 Trabajando con Sesiones.............................................................173 Primeros pasos con sesiones..................................................173 Crear una nueva sesi�n..........................................................174 Leer una sesi�n.......................................................................175 Modificar la sesi�n..................................................................175 Eliminar una variable de sesi�n..............................................175 Un caso pr�ctico de uso de sesiones..........................................176 Funciones necesarias..............................................................178 10 Programador PHP Experto Eugenia Bahit Funciones de acceso al sistema..........................................178 Funciones para destruir la sesi�n del usuario.....................179 Funciones para verificaci�n y validaci�n de sesiones.........180 La funci�n que redirige a los usuarios.................................182 Pasos finales...........................................................................1 82 Tratamiento y control de errores..............................184 Tipos de errores..........................................................................18 4 Configurando errores en tiempo de ejecuci�n............................186 Un ejemplo sencillo pero altamente productivo......................186 Utilizando el s�mbolo @ para silenciar errores............................187 Trabajando con Bases de Datos MySQL.....................189 Acerca de MySQL........................................................................190 Instalaci�n y configuraci�n de MySQL.....................................190 Iniciar, reiniciar y detener el servidor MySQL..........................191 Administraci�n de MySQL.......................................................193 Conectarse y desconectarse al servidor.............................193 Comandos para administrar MySQL dede el shell interactivo ................................................................................... .........193 Sobre el lenguaje SQL.................................................................195 Tipos de datos m�s comunes (recomendados).......................195 Sint�xis b�sica de las sentencias SQL.....................................196 Crear tablas en una base de datos.....................................196 Insertar datos en una tabla.................................................198 Seleccionar registros...........................................................198 Modificar registros..............................................................199 Eliminar registros................................................................200 Consultas avanzadas..............................................................201 La cl�usula WHERE.............................................................201 11 Programador PHP Experto Eugenia Bahit Ordenando consultas: la cl�usula ORDER BY......................203 Alias de tablas y campos........................................................203 Funciones del lenguaje SQL de MySQL....................................204 Contar la cantidad de registros: COUNT()...........................204 Sumar totales: SUM()..........................................................204 Concatenar cadenas: CONCAT()..........................................204 Convertir a min�sculas y may�sculas: LCASE() y UCASE(). 205 Reemplazar datos: REPLACE()............................................205 Obtener los primeros o �ltimos caracteres: LEFT() y RIGHT() ................................................................................... .........205 Redonder n�meros: ROUND().............................................205 Obtener solo la fecha de un campo DATETIME o TIMESTAMP: DATE()............................................................................. ....205 Obtener una fecha formateada: DATE_FORMAT()................205 Obtener el registro con el valor m�ximo y m�nimo: MAX() y MIN().............................................................................. .....206 Optimizaci�n de bases de Datos.................................................206 Todos los registros deben tener un ID �nico...........................206 Crear �ndices en las tablas......................................................207 Indica cu�les campos no pueden ser nulos.............................207 Utiliza el motor InnoDB...........................................................208 Obtener mayor informaci�n........................................................208 Trabajando con MySQL desde PHP............................209 MySQL desde PHP con el conector mysql....................................210 Conectarse a la base de datos................................................210 Seleccionar una base de datos...............................................211 Ejecutar una consulta simple..................................................211 Ejecutar una consulta de selecci�n m�ltiple y capturar sus resultados......................................................................... ......211 Capturamos el array con los resultados..............................211 12 Programador PHP Experto Eugenia Bahit Liberar los resultados..........................................................212 Cerrar la conexi�n...................................................................212 Algunos ejemplos concretos...................................................213 Consulta de selecci�n.........................................................213 Insertar varios registros en un solo paso............................213 MySQL desde PHP con el conector mysqli...................................215 Abrir una conexi�n mediante mysqli.......................................216 Preparar la consulta................................................................216 Ejecutar la consulta................................................................217 Cerrar la consulta....................................................................217 Cerrar la conexi�n...................................................................217 Ejemplo de inserci�n completo...............................................218 Capturar resultados de una consulta de selecci�n..................218 Ejemplo completo de consultas de selecci�n..........................219 Introducci�n al Paradigma de la programaci�n orientada a objetos................................................................221 Pensar en objetos........................................................................222 Y �qu� es un objeto?...............................................................222 Ahora �qu� me dices si describimos las cualidades de un objeto?............................................................................ ........223 Pero algunos objetos, tambi�n se componen de otros objetos... ................................................................................... .............224 Y tambi�n hay objetos que comparten caracter�sticas con otros objetos............................................................................ ........227 Los objetos, tambi�n tienen la capacidad de �hacer cosas�...230 Objetos y m�s objetos: la parte dif�cil.....................................231 Al pan, pan. Y al vino, vino. Las cosas por su nombre.........232 Programaci�n Orientada a Objetos.............................................233 Elementos y Caracter�sticas de la POO...................................233 13 Programador PHP Experto Eugenia Bahit Clases............................................................................. ....234 Propiedades........................................................................ 234 M�todos............................................................................ ..235 Objeto............................................................................. ....236 Herencia: caracter�stica principal de la POO.......................236 Accediendo a los m�todos y propiedades de un objeto..........237 Acceder a las propiedades de un objeto, dentro de la clase ................................................................................... .........237 Composici�n........................................................................ ...238 Ejemplo pr�ctico de Herencia vs. Composici�n...........................240 Programando con Historias de Usuario.....................242 Criterios de Aceptaci�n ..........................................243 Dividiendo Historias de Usuario en Tareas..........................244 Introducci�n a la Programaci�n eXtrema..................247 TDD � Test-Driven Development...............................248 �Qu� es el desarrollo -o programaci�n- guiado por pruebas?.....248 Test Unitarios.......................................................................... ....251 Caracter�sticas de los Test Unitarios........................................252 Anatom�a........................................................................... .....253 Algoritmo para escribir pruebas unitarias...............................257 PRIMER PASO: Escribir el Test y hacer que falle..................257 SEGUNDO PASO: Escribir la m�nima cantidad de c�digo para que el test pase..................................................................259 TERCER PASO: Escribir un nuevo test y hacer que falle......260 CUARTO PASO: Escribir el algoritmo necesario para hacer pasar el test........................................................................261 Unit Testing con PHPUnit.............................................................265 M�todos Assert de PHPUnit.....................................................265 14 Programador PHP Experto Eugenia Bahit Ejercicio.......................................................................... ............268 Unificaci�n del c�digo en Repositorios........................................269 Sobre los Sistemas de Control de Versiones...........................269 Integraci�n continua con Bazaar.............................................271 Instalaci�n de Bazaar..........................................................271 Bazaar por l�nea de comandos............................................272 Presentarse ante Bazaar.....................................................273 Iniciar un nuevo proyecto...................................................273 Clonar el repositorio central: crear los repositorios locales.273 Nociones b�sicas para integrar c�digo de forma continua. 275 Guardando el path del repo central....................................276 Integraci�n continua avanzada con Bazaar............................277 Resumen de comandos de uso frecuente...........................278 Resumen para uso diario de Bazaar............................................280 Refactoring.............................................................282 El problema........................................................................... ......282 La soluci�n........................................................................... .......283 Cu�ndo y c�mo tomar la desici�n de refactorizar...................284 Una soluci�n a cada problema................................................285 Variables de uso temporal mal implementadas..................285 M�todos que reciben par�metros.......................................288 Expresiones extensas.........................................................289 M�todos extensos...............................................................289 C�digo duplicado en una misma clase................................291 C�digo duplicado en varias clases con la misma herencia..292 C�digo duplicado en varias clases sin la misma herencia...293 Introducci�n a la Arquitectura de Software...............295 �Qu� es la arquitectura de software? .........................................295 15 Programador PHP Experto Eugenia Bahit Atributos de calidad ...................................................................295 Niveles de abstracci�n................................................................297 Estilo Arquitect�nico...............................................................298 Patr�n Arquitect�nico..............................................................299 Patr�n de Dise�o.....................................................................300 Introducci�n al Patr�n Arquitect�nico MVC...............301 Entendiendo el funcionamiento de MVC.....................................301 Modelos en MVC......................................................307 Objetos puros: caracter�sticas de un modelo..............................307 Creando modelos bajo MVC en PHP............................................308 Herencia y Composici�n..........................................................308 Acceso a bases de datos.........................................................310 C�digo fuente de una capa de abstracci�n a nivel del core311 Object Relational Mapping (ORM).......................................312 Ventajas y desventajas del utilizar ORMs................313 ORM Frameworks para PHP.....................................314 Bibliograf�a recomendada...........................................................314 Las vistas...............................................................316 �Por d�nde empezar a desarrollar las vistas?.............................316 Desarrollando la GUI...............................................................317 Componentes de la GUI......................................................317 Arquitectura....................................................................... .318 Preparando la GUI para interactuar con la l�gica................319 Comodines �Qu� son y c�mo implementarlos?.......320 Dise�ando la l�gica de negocios.............................................323 L�gica principal...................................................................324 L�gica a nivel de m�dulo....................................................334 16 Programador PHP Experto Eugenia Bahit Caracter�sticas m�nimas que debe tener la l�gica...334 Creando la l�gica de la vista para un modelo.........337 El controlador: el alma de MVC.................................340 Front Controller: controlando la aplicaci�n a nivel del core.........342 Configurando URLs amigables para nuestra aplicaci�n..........343 Configuraci�n de Apache....................................................344 Modificar el VirtualHost.......................................................345 Creando el archivo .htaccess..............................................346 Creando un Application Handler.............................................346 La clase AppHandler...........................................................347 Conclusi�n......................................................................... .350 La clase FrontController..........................................................350 Conclusi�n......................................................................... .353 Creando controladores para nuestros modelos...........................354 Notas adicionales sobre el ejemplo.............................................359 Completando la aplicaci�n..........................................................360 Inicializador del n�cleo...........................................................363 Los patrones de dise�o Front Controller y Application Handler en MVC......................................................366 Web Services: creaci�n de una API REST...................369 Conceptos b�sicos......................................................................369 Web Services..........................................................................3 69 API................................................................................ ...........369 REST............................................................................... ........369 Entendiendo las Arquitecturas REST...........................................370 Caracter�sticas de una API-REST.............................................370 Funcionamiento de la API REST de nuestra MVC App..................372 17 Programador PHP Experto Eugenia Bahit Objetivo del Web Service........................................................372 Formato de la URI...................................................................375 Dise�o de la API......................................................................376 18 Programador PHP Experto Eugenia Bahit Introducci�n a las tecnolog�as GLAMP GLAMP son las siglas de cuatro tecnolog�as libres, que conforman la base de las aplicaciones Web basadas en: � Sistema Operativo : GNU/Linux � Servidor Web : Apache � Servidor de bases de datos : MySQL � Lenguaje de programaci�n h�brido (multiparadigma) y de alto nivel: PHP GLAMP y LAMP La mayor�a de las veces, encontraremos bibliograf�a que al momento de referirse a las tecnolog�as GLAMP, suprimen la �G� del comienzo, cometiendo el grave error de llamarlas simplemente LAMP. De la misma forma, en una gran cantidad de casos, la documentaci�n se refiere al Sistema Operativo GNU/Linux, como �Linux�, suprimiendo las siglas �GNU�. Pero �Qu� tiene aquello de errado? La respuesta a esta pregunta, est� en la gran diferencia entre GNU/Linux y Linux. Diferencia entre GNU/Linux y Linux Linux, es un kernel, es decir, el n�cleo de un Sistema Operativo, mientras que GNU/Linux, el Sistema Operativo que utiliza el Kernel Linux como n�cleo. 19 Programador PHP Experto Eugenia Bahit El Kernel Linux, parte fundamental del Sistema Operativo, fue desarrollado por Linus Torvals, utilizando como modelo a UNIX. Una de las diferencias fundamentales entre los n�cleos Linux y UNIX, es que el primero, es Software Libre, mientras que el segundo no lo es. Por otra parte, mientras existe un �nico Kernel Linux (con versiones diferentes), existen decenas y hasta cientos de distribuciones GNU/Linux, es decir, diferentes Sistemas Operativos basados en el Kernel Linux, entre las cuales se destacan: Debian, Ubuntu, Kubuntu, Fedora, Gentoo, Slackware, CentOS, ArchLinux, Asturix, entre otros cientos. Free Software Foundation y el Proyecto GNU La Free Software Foundation, organizaci�n sin fines de lucro, fundada por Richard Stallman, principal precursor del Software Libre, es el organismo que cre�, difunde y promueve, el Sistema Operativo GNU/Linux, a trav�s del Proyecto GNU. Informaci�n complementaria Sitios Web de Referencia Sitio Web de la Free Software Foundation: www.fsf.org Sitio Web del Proyecto GNU: www.gnu.org Sitio Web del Kernel Linux: http://www.kernel.org/ Sitio Web de la Linux Foundation: http://www.linuxfoundation.org/ Bibliograf�a complementaria Introduccion al software libre.pdf (Universitat Obierta de 20 Programador PHP Experto Eugenia Bahit Catalunya) Sistema operativo gnu linux basico.pdf (Universitat Obierta de Catalunya) Preparaci�n b�sica del entorno de desarrollo En este curso, nos enfocaremos en tecnolog�as GLAMP, a partir de la distribuci�n Ubuntu 10.04 LTS (Lucid) -o superior- de GNU/Linux, basada en Debian. En caso de YA contar con otra distribuci�n, versi�n de Ubuntu o Debian, puedes saltar estos p�rrafos e ir directamente a la instalaci�n de Apache. Instalaci�n de Ubuntu GNU/Linux en Windows Si eres usuario de Windows y deseas conservar tu Sistema Operativo actual, puedes descargar Ubuntu Windows Installer desde el sitio Web oficial de Canonical (empresa que desarrolla y mantiene Ubuntu) en la siguiente URL: http://www.ubuntu.com/download/ubuntu/windows-installer Ubuntu Windows Installer se instalar� desde el propio MS Windows� como si fuese un Software m�s, permiti�ndote iniciar tu ordenador con Ubuntu o MS Windows� seg�n elijas. Para instalar Ubuntu Windows Installer, sigue las instrucciones de los pasos 2 y 3 de la URL de descarga, las cuales podr�s visualizar pulsando el bot�n �Show me how� de cada uno de los pasos. 21 Programador PHP Experto Eugenia Bahit Instalaci�n de Ubuntu GNU/Linux como �nico Sistema Operativo Para instalar Ubuntu como �nico Sistema Operativo, sigue los siguientes pasos: 1. ingresa en http://www.ubuntu.com/download/ubuntu/download 2. En el paso 1, selecciona la versi�n de Ubuntu que deseas descargar. Para procesadores de un solo n�cleo, selecciona la versi�n 10.04 LTS. Para procesadores m�s modernos, puedes seleccionar la �ltima versi�n (versi�n que aparece seleccionada por defecto en el desplegable de versiones). Si tienes dudas sobre si elegir la versi�n para 32 o 64 bits, elige la de 32-bits. Pulsa el bot�n �Start download� y aguarda a que se descargue el archivo. 3. Una vez descargado el archivo, podr�s quemarlo en un CD/DVD o un Pendrive USB. En el paso 2 de la URL de descarga, selecciona CD o USB stick seg�n tus preferencias y el Sistema Operativo desde el cual har�s la copia (Windows o Mac). Pulsa el bot�n �show me how� y sigue las instrucciones de quemado. 4. A continuaci�n, salta al paso 4 del sitio de descarga (el 3 es solo para probar Ubuntu sin instalarlo); pulsa el bot�n �show me how� y sigue las instrucciones para instalar Ubuntu en tu ordenador. Instalaci�n de Apache Server Antes de instalar Apache en tu distribuci�n GNU/Linux, crearemos un Lanzador de la terminal (llamado �acceso directo� en Windows), para ya tenerlo �a mano�. Para ello, sigue los siguientes pasos: 1. En el panel superior (donde figuran los men�es 22 Programador PHP Experto Eugenia Bahit �Aplicaciones, Lugares y Sistema�), haz clic derecho con el rat�n luego del men� �Sistema�, y selecciona la opci�n �A�adir al Panel�. 2. A continuaci�n, haz doble clic sobre la primera opci�n Lanzador de Aplicaci�n Personalizado. 3. En la ventana de creaci�n de lanzador, ingresa los datos como se muestra a continuaci�n: � Nombre: Terminal � Comando: gnome-terminal � Comentario: Abrir terminal en modo gr�fico 4. Pulsa el bot�n �Aceptar�. Ver�s ahora, el s�mbolo de la terminal en tu panel superior. Una vez creado el lanzador, vamos a continuar instalando Apache. Para ello, abre una terminal, pulsando una vez, sobre el �cono del lanzador, que acabas de crear. Una vez en la terminal, lo primero que haremos, ser� asegurarnos de tener actualizado el sistema operativo y de esta forma, securizarlo. Para ello, escribe el siguiente comando: sudo apt-get update Deber�s ingresar tu contrase�a. Mientras la escribas, no se mostrar� ning�n car�cter en la pantalla (ni siquiera asteriscos). SOBRE LOS COMANDOS sudo: te convierte en super usuario. �nico usuario que tiene permisos para instalar paquetes en tu sistema operativo. apt-get: es la utilidad para manejar paquetes en distribuciones GNU/Linux basadas en Debian. Alternativamente, puede utilizar el comando aptitude en 23 Programador PHP Experto Eugenia Bahit vez de apt-get. update: opci�n de apt-get que sincroniza los archivos del �ndice de paquetes con los repositorios oficiales (dicho de otra forma, obtiene un �ndice de actualizaciones) Una vez finalizada la sincronizaci�n del �ndice de actualizaciones, escribe: sudo apt-get upgrade Nuevamente deber�s ingresar tu contrase�a. Posiblemente, deban instalarse actualizaciones. Entonces, te preguntar� si deseas continuar. Deber�s pulsar la tecla y (de �yes�) y luego la tecla enter y esperar que finalicen las actualizaciones. Recuerda: siempre, antes de instalar cualquier paquete, debes ejecutar previamente, los comandos sudo apt-get update y luego sudo apt-get upgrade. Una vez actualizado el sistema operativo, procederemos a instalar Apache. Para ello, escribe el siguiente comando: sudo apt-get install apache2 SOBRE LOS COMANDOS install es la opci�n de apt-get que indica que se instalar� uno o m�s paquetes apache2 es el nombre del paquete que se instalar� 24 Programador PHP Experto Eugenia Bahit Ya tenemos el Sistema Operativo y el servidor Web instalado. Para asegurarnos de que Apache est� funcionando, vamos a escribir el siguiente comando en la terminal: sudo /etc/init.d/apache2 start TIP: Al escribir comandos, nombres de archivos y/o directorios en la terminal, pulsando la tecla de tabulaci�n, se autocompletan. Cuando al pulsar la tecla de tabulaci�n, un pitido es emitido, puede significar una de dos cosas: a) que el comando, nombre de archivo o directorio no se ha localizado; b) la m�s frecuente, que existen varias opciones posibles para autocompletar. Por eso, cuando un pitido sea emitido, pulsa la tecla de tabulaci�n dos veces consecutivas. Si existen varias opciones, te las mostrar� en pantalla. Otras opciones de Apache: Iniciar Apache: sudo /etc/init.d/apache2 start Apagar Apache: sudo /etc/init.d/apache2 stop Reiniciar Apache: sudo /etc/init.d/apache2 restart se debe utilizar siempre, tras realizar alguna modificaci�n a Apache. 25 Programador PHP Experto Eugenia Bahit Los cambios de configuraci�n de Apache, se realizan modificando el archivo apache2.conf que se encuentra en el directorio /etc/apache2/. Este archivo, solo puede modificarse, accediendo a �l, como super usuario: sudo gedit /etc/apache2/apache2.conf el comando gedit, abrir� el archivo con la aplicaci�n Gedit: un editor de textos. Recargar Apache: sudo /etc/init.d/apache2 reload se utiliza generalmente, cuando un nuevo sitio Web es configurado. It Works! Cuando Apache ha sido instalado e inicializado, podr�s ver la p�gina de bienvenida, ingresando la URL http://localhost o http://127.0.0.1 en tu navegador de Internet. Para abrir el navegador de Internet, ve a Aplicaciones > Internet > Navegador Web Firefox. Tambi�n puedes abrir Firefox desde la terminal, escribiendo firefox. Es posible tambi�n, abrir una URL en Firefox desde la terminal, escribiendo: firefox http://www.google.com (o la URL a la cual desees acceder) 26 Programador PHP Experto Eugenia Bahit Instalaci�n de MySQL (ver secci�n: PHP y MySQL m�s adelante) Instalaci�n de PHP Instalar PHP en Ubuntu es tan simple que solo requiere un comando. Aprovecharemos esta simplicidad, para �matar dos p�jaros de un tiro�, e instalar, adem�s de PHP, PHP-CLI: un int�rprete de l�nea de comando para PHP, que nos permitir� probar c�digo escrito en este lenguaje, utilizando un shell interactivo desde la terminal. Para instalar PHP y PHP-CLI, escribe: sudo apt-get install php5 php5-cli Verificando la versi�n de PHP En tu terminal, escribe: php -v Obtendr�s un resultado similar al siguiente: eugenia@cocochito:~$ php -v PHP 5.3.2-1ubuntu4.14 with Suhosin-Patch (cli) (built: Feb 11 2012 06:50:46) Copyright (c) 1997-2009 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies 27 Programador PHP Experto Eugenia Bahit Configurando el directorio de tu Web Local Por defecto, Apache espera que los archivos de tu sitio Web local, se alojen en el directorio /var/www/, pero trabajar en este directorio puede ser muy inc�modo, ya que solo podr�s acceder a �l, con permisos de super usuario. Para evitar este escoyo, tenemos dos formas diferentes de solucionarlo: � Opci�n 1: Crear un enlace simb�lico (llamado �acceso directo� en Windows), en la carpeta /var/www/, que redireccione hacia la carpeta de tu home, donde quieras hospedar los archivos de tu Web local. � Opci�n 2: Modificar el directorio ra�z de tu Web local y establecerlo apuntando hacia el directorio de tu home donde quieras hospedar los archivos de tu Web local. Esta segunda opci�n, ser� la forma que elegiremos en este curso. Sin perjuicio de ello, aprenderemos sobre como llevar adelante la opci�n 1. Crear el directorio para tu Web local Abre una terminal y escribe el comando cd ~ que te llevar� directamente a la home de tu usuario y a continuaci�n escribe el comando pwd que te indica en que directorio te encuentras actualmente. Recuerda que el comando cd sirve para moverte desde la terminal, por todos los directorios. Ver�s algo como esto: eugenia@cocochito:/etc/apache2$ cd ~ 28 Programador PHP Experto Eugenia Bahit eugenia@cocochito:~$ pwd /home/eugenia Lo anterior, indica que el directorio de la home de mi usuario, es /home/eugenia. El tuyo ser� /home/nombre_de_tu_usuario. Una vez all�, crearemos un directorio llamado curso-php, a fin de almacenar all�, todos los archivos que utilicemos en el curso para luego, convertirlo adem�s, en la home de tu Web local. Para crear el directorio, escribe lo siguiente: mkdir curso-php El comando mkdir es el utilizado para crear directorios. Modificando el directorio ra�z de tu Web local En la terminal, navega con el comando cd hasta el directorio de Apache donde se almacenan los archivos de configuraci�n de los sitios Web hospedados: cd /etc/apache2/sites-available/ A continuaci�n, lista los archivos de esa carpeta con el comando ls (ele ese) seguido de la opci�n -l (ele) que te permitir� listarlos uno debajo del otro (sin la opci�n -l, se mostrar�an uno al lado del otro). Ver�s algo como lo que sigue: eugenia@cocochito:~$ cd /etc/apache2/sites-available/ eugenia@cocochito:/etc/apache2/sites-available$ ls -l total 4 -rw-r--r-- 1 root root 960 2011-10-21 23:13 default El archivo default es el que modificaremos. Para modificar el 29 Programador PHP Experto Eugenia Bahit archivo default, necesitamos hacerlo como super usuario y lo abriremos con Gedit, escribiendo: sudo gedit default Una vez all�, localiza la l�nea que establece cu�l ser� la ra�z de tu Web local: DocumentRoot /var/www Y modif�cala por: DocumentRoot /home/tu-usuario/curso-php A continuaci�n, el bloque que establece directivas de configuraci�n especiales para el directorio ra�z de tu Web local. Dicho bloque, es el que comienza por: <Directory /var/www/> Reemplaza all�, /var/www/ por /home/tu-usuario/cursophp/ <Directory /home/tu-usuario/curso-php/> Guarda los cambios y cierra Gedit. Finalmente, tendremos que reiniciar Apache para que los cambios se vean reflejados. Para ello, en la terminal, escribe: sudo /etc/init.d/apache2 restart Como bien comentamos antes, hubiese sido posible, crear un enlace simb�lico en /var/www/ que apuntara a /home/tuusuario/ curso-php/. De haber optado por esta alternativa, nos hubi�semos valido 30 Programador PHP Experto Eugenia Bahit del comando ln (ele ene) con la opci�n -s, destinado a crear enlaces simb�licos (o symlinks). Como la escritura en el directorio /var/www/ est� restringida a usuarios con permiso de root (super usuarios), deber�amos haber ejecutado dicho comando anteponiendo el comando sudo. La sintaxis para crear enlaces simb�licos dentro de una carpeta, es: ln -s destino nombre_del_enlace_simbolico Donde destino ser� la ruta completa del directorio (o archivo) al que queremos apuntar, y nombre_del_enlace_simbolico el �alias� para ese symlink. Para crear un enlace simb�lico, llamado miweb, dentro del directorio /var/www/ y que apunte a /home/eugenia/cursophp/ deber�amos haber hecho lo siguiente: cd /var/www/ sudo ln -s /home/eugenia/curso-php/ miweb Entonces cuando accedi�ramos a /var/www/miweb/ hubi�semos estado viendo los archivos de /home/eugenia/curso-php/. Conociendo PHP Etiquetas de apertura y cierre Como se explic� anteriormente, existen dos posibilidades para 31 Programador PHP Experto Eugenia Bahit definir que un archivo debe ser interpretado en PHP:. Veremos aqu�, las ventajas y desventajas de cada uno de ellos. Opci�n #1 (recomendada): <?php // aqu� ir� todo el contenido en lenguaje PHP ?> Esta opci�n, se sugiere como alternativa recomendada, puesto que independientemente del valor establecido en short_open_tag en el php.ini, funcionar� por defecto y sin necesidad de modificar el archivo php.ini, en cualquier servidor. Por otro lado, la utilizaci�n de esta alternativa, trae aparejadas las siguientes ventajas: 1. Permite la utilizaci�n de XML en el servidor. El lenguaje XML utiliza como etiquetas de apertura y cierre, <? y ?>. Alternativamente, permite tambi�n <?xml y ?>. Por lo tanto, utilizando <?php se permite ejecutar c�digo XML como tal. 2. Evita tener que embeber1 c�digo XML dentro de PHP 3. Es una forma de definir un lenguaje estandar de PHP. AVISO: Para poder utilizar XML (adem�s de PHP), se recomienda establecer el valor de short_open_tag en Off, en el archivo php.ini, puesto que el valor por defecto se encuentra establecido en On. 1 Embeber c�digo se refiere a hacer un print (o echo) con PHP, para escribir utilizando otro lenguaje. Es una de las pr�cticas de programaci�n m�s desaconsejadas, ya que dificulta la lectura de c�digo haciendo dif�cil la escalabilidad y mantenimiento de aplicaciones. 32 Programador PHP Experto Eugenia Bahit Opci�n #2: <? // aqu� ir� todo el contenido en lenguaje PHP ?> Esta alternativa, representa una forma abreviada de las etiquetas anteriores. Utilizarla, requiere de configurar el archivo php.ini, estableciendo el valor de short_open_tag a On. ; This directive determines whether or not PHP will recognize code between ; <? and ?> tags as PHP source which should be processed as such. It's been ; recommended for several years that you not use the short tag "short cut" and ; instead to use the full <?php and ?> tag combination. With the wide spread use ; of XML and use of these tags by other languages, the server can become easily ; confused and end up parsing the wrong code in the wrong context. But because ; this short cut has been a feature for such a long time, it's currently still ; supported for backwards compatibility, but we recommend you don't use them. ; Default Value: On ; Development Value: Off ; Production Value: Off ; http://php.net/short-open-tag short_open_tag = On Puede leer m�s informaci�n sobre short_open_tag en http://www.php.net/manual/es/ini.core.php#ini.short-open-tag AVISO: N�tese que no existe posibilidad de modificar short_open_tag en tiempo de ejecuci�n. Conocer el valor actual de short_open_tag Para conocer el valor actual de short_open_tag, ejecute el siguiente comando en una terminal: php -r 'echo phpinfo();' | grep short_open_tag 33 Programador PHP Experto Eugenia Bahit Una forma resumida de lo anterior, puede ser tambi�n: php -i | grep short_open_tag d�nde el par�metro -i da la misma salida que phpinfo() Tambi�n puede buscar este valor, directamente en el archivo php.ini: grep short_open_tag /etc/php5/apache2/php.ini AVISO: Reemplace /etc/php5/apache2/php.ini por la ruta del php.ini en su servidor, de ser necesario. Instrucciones y estructuras Existen dos tipos de instrucciones en PHP: aquellas instrucciones que se ejecutan en una �nica l�nea y las estructuras de control que almacenan dichas instrucciones. Las instrucciones simples, siempre deben finalizar con un punto y coma (;) mientras que las estructuras de control, encerrer�n dichas instrucciones entre llaves { }. <?php estructura de control { instrucci�n 1; instrucci�n 2; } ?> Identaci�n en PHP PHP es un lenguaje que no requiere de identaci�n (sangrado) para ser interpretado. Por el contrario, todo el c�digo fuente 34 Programador PHP Experto Eugenia Bahit PHP puede ser escrito sin identaci�n, aunque esto, es una pr�ctica desaconsejada, ya que al igual que el c�digo embebido, dificulta la lectura y la consecuente escalabilidad y mantenimiento de la app. Estandarizaci�n de c�digo Como regla de estilo, se sugiere utilizar identaci�n de 4 espacios en blanco y dejar una l�nea en blanco, entre estructuras de control. La identaci�n, es utilizada para diferenciar estructuras de control y algoritmos, dentro del c�digo fuente: <?php estructura de control 1 { instrucci�n a; estructura de control 1.1 { instrucci�n b; estructura de control 1.1.1 { instrucci�n c; } } estructura de control 1.2 { instrucci�n d; } } estructura de control 2 { instrucci�n e; } ?> Impresi�n en pantalla En PHP, existen varias funciones para imprimir contenido en pantalla. Las dos funciones b�sicas son echo y print. 35 Programador PHP Experto Eugenia Bahit Estandarizaci�n de c�digo Como regla de estilo se sugiere optar por una de ellas y no utilizar ambas funciones en una misma app. <?php echo "Hola Mundo"; ?> Imprime Hola Mundo en pantalla. <?php print "Adi�s Mundo"; ?> Imprime Adi�s Mundo en pantalla. 36 Programador PHP Experto Eugenia Bahit Variables y tipos de datos b�sicos Una variable es elemento destinado a almacenar datos. �sta, puede almacenar datos num�ricos (enteros o flotantes), cadenes de texto, booleano (verdadero [true] o falso [false]), etc. Una variable se define (es decir, se crea), se le asigna un valor (es decir, se almacenan datos), puede modificarse (cambiar de valor) y eliminarse. Definici�n de una variable: Las variables en PHP se definen anteponiendo el sigo d�lar ($) seguido del nombre que se le quiera dar a esta. Nombre de las variables: El nombre de �stas, debe guardar ciertas reglas: � Solo pueden comenzar por car�cter alfab�tico o gui�n bajo (_) � El nombre puede estar conformado por may�sculas, min�sculas, guiones bajos (_) y n�meros Asignaci�n de valores: para asignar valor a una variable, se coloca el sigo igual (=) segudio del valor. Tipos de datos: cuando el valor de una variable, es una cadena de texto, �ste, debe escribirse entre comillas dobles ("), aunque tambi�n entre comillas simples ('). A fin de estandarizar el c�digo, utilizaremos siempre comillas dobles para cadenas de texto. Tanto los valores num�ricos (ya sean �stos, n�meros enteros o de coma flotante) y los booleanos, no requieren ser entre-comillados. 37 Programador PHP Experto Eugenia Bahit AVISO: Para los n�meros de coma flotante se utiliza el punto (.) y NO la coma (,) Veamos un ejemplo de definici�n y asignaci�n de variables: <?php $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio = 3.75; $vence = False; $hay_stock = True; $stock_en_deposito_1 = 20; $stock_en_deposito_27 = 5; $stock_en_deposito_73A = 54; ?> Es posible adem�s, asignar a una variable el valor de otra variable: <?php $nombre_de_producto_por_defecto = "Producto alimenticio"; $nombre_producto = $nombre_de_producto_por_defecto; echo $nombre_producto; // imprime: Producto alimenticio ?> Tambi�n es posible, insertar el valor de una variable, dentro de una cadena de texto: <?php $nombre_de_producto_por_defecto = "Producto"; $nombre_producto = "$nombre_de_producto_por_defecto en oferta"; echo $nombre_producto; // imprime: Producto en oferta ?> Pero �qu� sucede si se necesita concatenar el valor de una variable a una cadena de texto pero sin mediar espacios? 38 Programador PHP Experto Eugenia Bahit Por ejemplo, si en el caso anterior, se desea que $nombre_producto sea "Productos en oferta". Estos casos, se resuelven envolviendo dicha variable entre llaves: <?php $nombre_de_producto_por_defecto = "Producto"; $nombre_producto = "{$nombre_de_producto_por_defecto}s en oferta"; echo $nombre_producto; // imprime: Productos en oferta ?> En PHP, tambi�n es posible concatenar variables mediante el operador de concatenaci�n "punto" (.): <?php $nombre_de_producto_por_defecto = "Producto"; $nombre_producto = $nombre_de_producto_por_defecto . " en oferta"; echo $nombre_producto; // imprime: Producto en oferta ?> Aunque esta �ltima pr�ctica, debe utilizarse responsablemente, puesto que en determinadas ocasiones puede resultar dif�cil de leer y descifrar la salida final que tendr�: <?php $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio = 3.75; $vence = False; $hay_stock = True; $stock_en_deposito_1 = 20; $stock_en_deposito_27 = 5; $stock_en_deposito_73A = 54; $detalles_del_producto = "(" . $codigo_de_producto . ") " . $nombre_producto . ". Precio: USD " . $precio . ".-"; ?> El ejemplo anterior, podr�a resultar m�s legible, de la siguiente forma: <?php $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio = 3.75; 39 Programador PHP Experto Eugenia Bahit $vence = False; $hay_stock = True; $stock_en_deposito_1 = 20; $stock_en_deposito_27 = 5; $stock_en_deposito_73A = 54; $detalles_del_producto = "($codigo_de_producto) $nombre_producto. Precio: USD $precio.-"; ?> Para modificar una variable, reemplazando su valor, solo basta con reasignarle datos: <?php $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio = 3.75; $vence = False; $hay_stock = True; $stock_en_deposito_1 = 20; $stock_en_deposito_27 = 5; $stock_en_deposito_73A = 54; $detalles_del_producto = "($codigo_de_producto) $nombre_producto. Precio: USD $precio.-"; echo $detalles_del_producto; $detalles_del_producto = "No hay detalles definidos"; print $detalles_del_producto; ?> Null, var_dump() e isset() No solo es posible modificar el valor de una variable. Tambi�n es posible: a) vaciarla manteniendo su tipo: <?php $producto = "Coca-Cola x 1,5 Lts."; $producto = ""; ?> 40 Programador PHP Experto Eugenia Bahit b) vaciarla sin conservar su tipo: <?php $producto = "Coca-Cola x 1,5 Lts."; $producto = NULL; ?> c) o, eliminarla (destruirla) por completo: <?php $producto = "Coca-Cola x 1,5 Lts."; unset($producto); ?> En todo momento, PHP nos permite conocer el tipo y valor de una variable, mediante la funci�n var_dump(): <?php $producto = "Coca-Cola x 1,5 Lts."; var_dump($producto); # salida: string(20) "Coca-Cola x 1,5 Lts." $producto = ""; var_dump($producto); # salida: string(0) "" $producto = NULL; var_dump($producto); # salida: NULL unset($producto); var_dump($producto); /* Generar� un error, ya que la variable $producto ha sido destruida Salida: PHP Notice: Undefined variable: producto ... NULL */ ?> var_dump() imprimir� los resultados en pantalla (tipo y valor de una variable), pero tambi�n, es posible conocer el tipo de una variable (no su valor), sin imprimirlo en pantalla, con 41 Programador PHP Experto Eugenia Bahit gettype(): <?php $a = 25; $tipo_a = gettype($a); echo $tipo_a; #imprimir� integer ?> Es muy �til adem�s, saber si una variable ha sido definida (y no se ha destruido con unset()) y tiene un tipo asignado (es decir, no es NULL). Para ello, dispones de la funci�n isset(). Esta funci�n, devolver� True si ha sido definida y no es NULL. De lo contrario, retornar� False: <?php $producto = "Coca-Cola x 1,5 Lts."; echo isset($producto); # retorna True $producto = ""; echo isset($producto); # Retorna True $producto = NULL; echo isset($producto); # retorna False unset($producto); echo isset($producto); # retorna False ?> �NULL o unset()? �Cu�l de los dos usar? Cuando una variable ya no es necesaria, debe priorizarse el uso de unset sobre NULL, ya que con unset(), se libera la direcci�n de la memoria en la cual hab�a sido escrita dicha variable. 42 Programador PHP Experto Eugenia Bahit Malas pr�cticas con variables, que afectan el uso de memoria Es posible tambi�n, agregar a una variable, otros datos al final de la cadena. Para ello, se utiliza el signo punto (.) antecediendo al signo igualdad (=): <?php $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio = 3.75; $vence = False; $hay_stock = True; $stock_en_deposito_1 = 20; $stock_en_deposito_27 = 5; $stock_en_deposito_73A = 54; $detalles_del_producto = "("; $detalles_del_producto .= $codigo_de_producto; $detalles_del_producto .= ") "; $detalles_del_producto .= $nombre_producto; $detalles_del_producto .= " Precio: USD "; $detalles_del_producto .= $precio; $detalles_del_producto .= ".-"; echo $detalles_del_producto; ?> Pero esta pr�ctica, reduce el rendimiento de la aplicaci�n, ya que cada instrucci�n, ser� almacenada en una direcci�n de memoria diferente, mientras que de la forma anterior, solo requiere una direcci�n de memoria para su almacenamiento. 43 Programador PHP Experto Eugenia Bahit Operadores aritm�ticos PHP permite realizar operaciones aritm�ticas de lo m�s variadas y por consiguiente, utilizar PHP "como calculadora". Para ello, disponemos de los siguientes operadores aritm�ticos: Operadores aritm�ticos Ejemplo Nombre Resultado -$a Negaci�n Opuesto de $a. $a + $b Adici�n Suma de $a y $b. $a - $b Sustracci�n Diferencia de $a y $b. $a * $b Multiplicaci�n Producto de $a y $b. $a / $b Divisi�n Cociente de $a y $b. $a % $b M�dulo Resto de $a dividido por $b. Referencia del manual oficial de PHP: http://www.php.net/manual/es/language.operators.arithmetic.p hp Haciendo c�lculos con el IVA <?php $alicuota_iva = 21; $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio_bruto = 3.75; $iva = 3.75 * 21 / 100; $precio_neto = $precio_bruto + $iva; ?> Estandarizaci�n de c�digo Utiliza nombres descriptivos para las variables; Si el nombre es compuesto, separa cada palabra por un gui�n bajo; 44 Programador PHP Experto Eugenia Bahit Escribe los nombres de variables en min�sculas; Cuando debas asignar m�ltiples valores a una variable, utiliza una sola instrucci�n toda vez que sea posible; Utiliza comillas dobles para encerrar las cadenas de texto, en vez de comillas simples; Utiliza espacios en blanco antes y despu�s de un operador aritm�tico para facilitar la lectura; settype �un bug no resuelto o feature objetable? PHP, asume que un n�mero encerrado entre comillas es l�gicamente, una cadena de texto: __eugenia_1978_esAR__@mydream:~$ php -r '$a = "33"; var_dump($a);' string(2) "33" Sinembargo, realizar� operaciones aritm�ticas de forma correcta, aunque alguno de los n�meros, sea de tipo string: __eugenia_1978_esAR__@mydream:~$ php -r '$a = "33"; $b = 10; echo $a + $b;' 43 No obstante, si se intenta realizar una operaci�n aritm�tica con cadenas de texto, que adem�s de n�meros, incluyan otro caracter, PHP, en vez de fallar y avisarnos del error, pasar� por alto la variable conflictiva: __eugenia_1978_esAR__@mydream:~$ php -r '$a = "E33"; $b = 10; echo $a + $b;' 10 Existe una funci�n para convertir el tipo de una variable settype($variable, "nuevo_tipo"): __eugenia_1978_esAR__@mydream:~$ php -a 45 Programador PHP Experto Eugenia Bahit Interactive shell php > $a = "33 manzanas"; php > settype($a, "integer"); php > var_dump($a); int(33) php > Utilizando settype, "podr�amos" asegurarnos realizar operaciones aritm�ticas seguras: <?php $a = "33 manzanas"; $b = 10; settype($a, "integer"); echo $a + $b; # salida: 43 ?> Sin embargo �prueba que sucede al ejecutar este c�digo? $a = "manzanas 33"; $b = 10; settype($a, "integer"); echo $a + $b; AVISO: No conf�es en settype() para efectuar operaciones aritm�ticas. Es preferible evitar su uso para estos casos. 46 Programador PHP Experto Eugenia Bahit HTML y PHP en un mismo archivo Como se coment� anteriormente, es posible "mezclar" c�digo HTML con PHP, sin necesidad de imprimir etiquetas HTML mediante PHP. Es decir, conservar la independencia de ambos lenguajes en el mismo archivo. El mejor procedimiento para hacer esto, es comenzar escribiendo el c�digo HTML y utilizando comentarios para recordarnos d�nde debemos insertar el c�digo PHP. En la programaci�n funcional o estructurada, �ste, es el mejor m�todo. Puesto que nos asegura, la mayor legibilidad posible. Vamos a ver un ejemplo de aquello que no debe hacerse y sugerir una mejor pr�ctica. Una mala pr�ctica: colocar c�digo HTML embebido dentro de variables de PHP <?php $alicuota_iva = 21; $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio_bruto = 3.75; $iva = 3.75 * 21 / 100; $precio_neto = $precio_bruto + $iva; $producto = "<p><b>Producto:</b> ($codigo_de_producto) $nombre_producto<br/> <b>Precio:</b> USD $precio_neto.- (IVA incluido)</p>"; ?> <!doctype html> <html> <head> <title>Detalles del producto <?php echo $nombre_producto; ?></title> </head> <body> <?php echo $producto; ?> </body> </html> 47 Programador PHP Experto Eugenia Bahit Una buena pr�tica para evitar lo anterior <?php $alicuota_iva = 21; $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio_bruto = 3.75; $iva = 3.75 * 21 / 100; $precio_neto = $precio_bruto + $iva; ?> <!doctype html> <html> <head> <title>Detalles del producto <?php echo $nombre_producto; ?></title> </head> <body> <p><b>Producto:</b> (<?php echo $codigo_de_producto; ?>) <?php echo $nombre_producto; ?><br/> <b>Precio:</b> USD <?php echo $precio_neto; ?>.- (IVA incluido)</p> </body> </html> AVISO: N�tese que la instrucci�n <?=$foo; ?> es una abreviaci�n de <?php echo $foo; ?> la cual solo se ejecutar� de forma satisfactoria desde la versi�n 5.4 de PHP, aunque el valor de short_open_tag sea Off. En versiones anteriores, se debe utilizar <?php echo $foo; ?> o en su defecto, establecer en On, el valor de short_open_tag en php.ini 48 Programador PHP Experto Eugenia Bahit Comentando y documentando el c�digo fuente Una de las pr�cticas m�s recomendadas, consiste en "comentar" el c�digo fuente. Comentar el c�digo, significa "escribir referencias sobre el c�digo fuente que nos ayuden a entenderlo". En PHP, existen dos tipos de comentarios: 1. Los comentarios de una sola l�nea 2. Los comentarios de varias l�neas (o docstrings) Los comentarios de una sola l�nea, pueden escribirse antecedidos de una doble barra diagonal (//) o una almohadilla (#): <?php // al�cuota del IVA (en porcentaje) $alicuota_iva = 21; // Datos del producto $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio_bruto = 3.75; # precio sin IVA // C�lculos relacionados al IVA $iva = 3.75 * 21 / 100; $precio_neto = $precio_bruto + $iva; # Precio con IVA incluido ?> Otra utilidad de comentar el c�digo fuente, es recomendarnos aquello que nos queda por hacer. Para ello, se utiliza la palabra TODO (del ingl�s "to do" que en espa�ol significa "por hacer") al comentario: <?php // al�cuota del IVA (en porcentaje) $alicuota_iva = 21; // Datos del producto $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; 49 Programador PHP Experto Eugenia Bahit $precio_bruto = 3.75; # precio sin IVA // C�lculos relacionados al IVA $iva = 3.75 * 21 / 100; $precio_neto = $precio_bruto + $iva; # Precio con IVA incluido # TODO calcular descuentos por cantidad ?> En cambio, los comentarios de varias l�neas se encierran entre /* y */ (como en CSS, por ejemplo): <?php // al�cuota del IVA (en porcentaje) $alicuota_iva = 21; // Datos del producto $codigo_de_producto = 1284; $nombre_producto = "Agua Mineral Manantial x 500 ml"; $precio_bruto = 3.75; # precio sin IVA // C�lculos relacionados al IVA $iva = 3.75 * 21 / 100; $precio_neto = $precio_bruto + $iva; # Precio con IVA incluido /* TODO calcular descuentos por cantidad para ello, tener en cuenta los siguientes porcentajes de descuento: 1 a 5 productos: 0% 6 a 10 productos: 2.5% 11 a 25 productos: 6.2% M�s de 25: 10% */ ?> 50 Programador PHP Experto Eugenia Bahit Inclusi�n de archivos en PHP PHP nos permite insertar cualquier tipo de archivos con formato de texto, dentro de un archivo PHP. Entre los tipos de archivos que podemos insertar dentro de un fichero .php, se encuentran aquellos con las siguientes extensiones: .php, .txt, .htm, .html, entre otros con formato de texto. Para insertar archivos, PHP dispone de cuatro funciones: 1. include: http://www.php.net/manual/es/function.include.php 2. include_once: http://www.php.net/manual/es/function.include-once.php 3. require: http://www.php.net/manual/es/function.require.php y 4. require_once: http://www.php.net/manual/es/function.require-once.php Estas cuatro funciones, necesitan recibir como par�metro2, la ruta local o remota del archivo a ser incluido. Ejemplos de inclusi�n de archivos locales: <?php include("archivo.php"); include_once("archivo.txt"); require("archivo.html"); require_once("archivo.htm"); 2 M�s adelante, cuando hablemos de funciones, veremos detenidamente qu� es y para qu� sirve un par�metro. Por el momento, cuando hablemos de par�metro, entenderemos que se trata de un "valor" que debemos pasar/enviar a una funci�n. 51 Programador PHP Experto Eugenia Bahit ?> Ejemplos de inclusi�n de archivos remotos: <?php include("http://www.miweb.com/archivo.php?foo=bar"); include_once("http://www.miweb.com/archivo.php"); require("http://www.miweb.com/archivo.html"); require_once("http://www.miweb.com/archivo.txt"); ?> Diferencia entre inclusi�n de archivos remotos y locales Una diferencia fundamental, entre incluir archivos remotos y archivos locales, es que los archivos PHP remotos, ser�n interpretados previamente en el servidor de origen y "servidos" al servidor de destino (el que los incluye), ya interpretados. Sin embargo, cuando un archivo PHP local es incluido, no ser� previamente interpretado, sino que de eso, se encargar� el archivo que lo incluy�. Es decir, que si queremos incluir el archivo mi_fichero.php ya interpretado conforme el valor del par�metro "foo", si utilizamos: include("mi_fichero.php?foo=15"); PHP arrojar� un error, ya que buscar� un archivo llamado mi_fichero.php?foo=15 en vez de interpretarlo. Sin embargo, podremos incluir remotamente para que se nos devuelva el archivo interpretado, mediante: include("http://miweb.com/mi_fichero.php?foo=15"); No obstante, PHP dispone de una funci�n alternativa SOLO para 52 Programador PHP Experto Eugenia Bahit estos casos (inclusi�n de archivos remotos ya interpretados) que incluso permite almacenar los datos recibidos en una variable: $contenido = file_get_contents("http://miweb.com/mi_fichero.php?foo=15"); Diferencia entre include y require Si bien las funciones require() e include() de PHP realizan una acci�n similar (importar un archivo), no son iguales. include() intenta importar al archivo indicado y en caso de no poder hacerlo, arroja un error y contin�a ejecutando el resto del script. Sin embargo, la funci�n require(), cuando no logra importar el archivo indicado, arroja un error y finaliza sin permitir que el resto del script contin�e ejecut�ndose. Include y require "_once" La �nica diferencia que existe entre include e include_once y require y require_once, es que si el archivo indicado con "_once" ya ha sido incluido no volver� a importarse. 53 Programador PHP Experto Eugenia Bahit Estructuras de Control � Parte I (condicionales) Definici�n Una estructura de control es un bloque de c�digo que permite tomar decisiones de manera din�mica, sobre c�digo existente. Condicionales if, else y else if El condicional if, al igual que otras estructuras de control, permite tomar decisiones, partiendo de la base de evaluar si una determinada condici�n se cumple. El razonamiento de condicionales puede representarse como sigue: si condicion X se cumple { hacer esto } sino, si condicion Y se cumple: { hacer esto otro } si no se cumple ni X ni Y { hacer tal otra cosa } No necesariamente el condicional debe cumplir la estructura anterior. A veces solo es necesario evaluar una �nica condici�n y tomar una decisi�n SOLO sobre la base de si esta condici�n se cumple: si condici�n X se cumple { hacer esto; // fin de la evaluaci�n } La sintaxis b�sica para los condicionales en PHP, se resume en: 54 Programador PHP Experto Eugenia Bahit if (condici�n A) { // algoritmo si se cumple condici�n A } else if (condici�n B) { // algoritmo si se cumple condici�n B } else { // algoritmo si no se cumplen las condiciones anteriores } Operadores l�gicos y de comparaci�n Para evaluar condiciones, no solo podemos recurrir a si "A es igual a B". Existen otros operadores que nos permiten evaluar diferentes condiciones. Estos operadores se denominan operadores l�gicos y son aquellos que nos permiten evaluar m�ltiples condiciones en un mismo proceso, mientras que se denominan operadores de comparaci�n, a aquellos que utilizamos para evaluar (comparar) la relaci�n existente enntre elementos. Operadores de comparaci�n Ejemplo Nombre Resultado $a == $b Igual TRUE si $a es igual a $b despu�s de la manipulaci�n de tipos. $a === $b Id�ntico TRUE si $a es igual a $b, y son del mismo tipo. $a != $b Diferente TRUE si $a no es igual a $b despu�s de la manipulaci�n de tipos. $a <> $b Diferente TRUE si $a no es igual a $b despu�s de la manipulaci�n de tipos. $a !== $b No id�ntico TRUE si $a no es igual a $b, o si no son del mismo tipo. $a < $b Menor que TRUE si $a es estrictamente menor que $b. $a > $b Mayor que TRUE si $a es estrictamente mayor que $b. $a <= $b Menor o igual que TRUE si $a es menor o igual que $b. $a >= $b Mayor o igual que TRUE si $a es mayor o igual que $b. 55 Programador PHP Experto Eugenia Bahit Diferencia entre igualdad e id�ntico en la comparaci�n AVISO: N�tese que la diferencia principal entre igualdad (==) e id�ntico (===), es que el primero convierte ambos elementos al mismo tipo (anulando as�, la comparaci�n de tipos, y arrojando que 10 ser� igual que "10"), mientras que el segundo, compara adem�s si ambos elementos pertenecen al mismo tipo. Lo anterior, tambi�n aplica a != y !==. Veamos algunos ejemplos: $a = 10; $b = "10"; $c = 11; $d = 0; $e = False; $a == $b True $d == $e True $a === $b False $d === $e False $a != $b False $d != $e False $a !== $b True $d !== $e True Operadores l�gicos Ejemplo Nombre Resultado $a and $b And (y) TRUE si tanto $a como $b son TRUE. $a or $b Or (o inclusivo) TRUE si cualquiera de $a o $b es TRUE. $a xor $b Xor (o exclusivo) TRUE si $a o $b es TRUE, pero no ambos. ! $a Not (no) TRUE si $a no es TRUE. $a && $b And (y) TRUE si tanto $a como $b son TRUE. $a || $b Or (o inclusivo) TRUE si cualquiera de $a o $b es TRUE. En el manual oficial de PHP http://www.php.net/manual/es/language.operators.logical.php, podemos ver una leyenda que dice: 56 Programador PHP Experto Eugenia Bahit "La raz�n para tener las dos variaciones diferentes de los operadores "and" y "or" es que ellos operan con precedencias diferentes." Lo cierto, es que tanto el operador and como el operador && poseen una precedencia izquierda (es decir, que la condici�n es evaluada de izquierda a derecha) y lo mismo sucede con or y ||. Creando condicionales Evaluando una �nica condici�n: $a = 10; if ($a == 10) { echo "\$a es igual a 10"; } Iniciar A en 10 si (A es igual a 10), entonces imprimir 'A es igual a 10' O tambi�n, tomar una decisi�n si la condici�n se cumple y otra si no se cumple: $a = 10; if ($a == 10) { echo "\$a es igual a 10"; } else { echo "\$a NO es igual a 10"; } Tambi�n podemos conbinar else e if, para tomar tantas decisiones como condiciones quieran evaluarse: $a = 15; if ($a == 10) { 57 Programador PHP Experto Eugenia Bahit echo "\$a es igual a 10"; } else if ($a == 12) { echo "\$a es igual a 12"; } else if ($a == 15) { echo "\$a es igual a 15"; } Y si a lo anterior le agregamos else estar�amos cubriendo todas las posibilidades: $a = 15; if ($a == 10) { echo "\$a es igual a 10"; } else if ($a == 12) { echo "\$a es igual a 12"; } else if ($a == 15) { echo "\$a es igual a 15"; } else { echo "\$a NO es ni 10 ni 12 ni 15"; } AVISO: Se debe tener especial cuidado en la comparaci�n de n�meros reales (flotantes). Veremos esto con detenimiento m�s adelante. Para mayor informaci�n, visitar la documentaci�n oficial en: http://www.php.net/manual/es/language.types.float.php#language .types.float.comparison 58 Programador PHP Experto Eugenia Bahit Estructuras de control con Switch Switch es una de las estructuras de control, que mejor se disponen para programar la toma de decisiones basadas en la comparaci�n de un �nico elemento. Switch se asemeja sutilmente a la evaluaci�n de condiciones mediante "else if", pero diferenci�ndose notablemente por su aplicaci�n en la pr�ctica, ya que es verdaderamente �til en aquellos casos donde la utilizaci�n de else if resulte redundante. B�sicamente, con Switch, lo que hacemos es evaluar una �nica expresi�n o variable, y tomar diversas decisiones en base a los diferentes posibles valores de la misma: $numero_dia = date('N'); /* la funci�n nativa date() de PHP, permite dar formato a la fecha local N, retorna un n�mero de 1 a 7, que representa el n�mero de d�a de la semana, siendo 1 Lunes y 7 domingo. Esta funci�n, ser� vista con detenimiento, m�s adelante. Para mayor informaci�n, visitar la documentaci�n oficial en http://www.php.net/manual/es/function.date.php */ $nombre_dia = ''; switch ($numero_dia) { case 1: $nombre_dia = "Lunes"; break; case 2: $nombre_dia = "Martes"; break; case 3: $nombre_dia = "Mi�rcoles"; break; case 4: $nombre_dia = "Jueves"; break; case 5: $nombre_dia = "Viernes"; break; 59 Programador PHP Experto Eugenia Bahit case 6: $nombre_dia = "S�bado"; break; case 7: $nombre_dia = "Domingo"; break; default: $nombre = "No sabemos que d�a es"; } El funcionamiento de switch puede resultar complejo de entender en un comienzo. Pero va a ir paso a paso. Switch tiene una sintaxis b�sica que se compone de: switch ($variable) { case "posible valor 1": // algoritmo a ejecutar si $variable == "posible valor 1" break; case "posible valor 3": // algoritmo a ejecutar si $variable == "posible valor 3" break; default: // algoritmo a ejecutar si valor no ha sido contemplado en // ning�no de los �case� anteriores } Cada case representa un "caso" (el posible valor que pueda retornar la variable evaluada). La palabra clave reservada break, "rompe" la ejecuci�n de la estructura. Es decir, que cuando un case es evaluado como verdadero, tras ejecutar el algoritmo de ese case, la palabra clave reservada break indica la finalizaci�n de toda la estructura (es decir, no se contin�an evaluando los siguientes "case". La palabra clave reservada default, contendr� el algoritmo a ser ejecutado, cuando la evaluaci�n de todos los "case" anteriores, haya sido False. 60 Programador PHP Experto Eugenia Bahit A fin de evitar la redundancia en el c�digo (por ejemplo, si se debiara aplicar el mismo algoritmo a dos o m�s valores distintos), switch nos permite agrupar los casos: switch ($variable) { case "posible valor 1": case "posible valor 2": case "posible valor 3": /* algoritmo a ejecutar si el valor de $variable es posible valor 1, posible valor 2 o posible valor 3 */ break; case "posible valor 4": /* algoritmo a ejecutar si el valor de $variable es posible valor 4 */ break; default: // algoritmo a ejecutar si valor no ha sido contemplado en // ning�no de los �case� anteriores } �Cu�ndo utilizar if y cuando switch? Si bien el uso de una u otra estructura, depender� de la desici�n de cada programador, existe un sentido l�gico que debe priorizarse para decidir su uso. Para ello, hay que tener en cuenta que: if nos sirve para evaluar "condiciones" y comparar m�ltiples elementos, mientras que switch, solo permite la evaluaci�n de un �nico elemento o expresi�n. Por lo tanto, toda vez que solo se requiera la evaluaci�n de un �nico elemento o expresi�n, se mayormente (con ciertas excepciones), se utilizar� switch y se optar� por if, cuando el uso de switch no sea posible. 61 Programador PHP Experto Eugenia Bahit Tipos de Datos complejos: Matrices simples y multidimensionales Matrices en PHP Una matriz (array) es un mapa de datos ordenado que asocia "claves" a sus valores correspondientes. Es as�, que estas matrices, nos son de gran utilidad para crear desde diccionarios de datos hasta �rboles de m�ltiples diccionarios. Sintaxis b�sica La sintaxis b�sica se compone de: array(clave => valor, ); Donde clave, puede ser un entero: $nombres_de_mujer = array(0 => 'Ana', 1 => 'Gabriela', 2 => 'Cecilia', ); O una cadena de texto: $telefonos_de_amigos = array('Juan' => '15 4017-2530', 'Javier' => '4921 � 1200',); Y valor, cualquier tipo de dato: $datos_de_juan = array('apellido' => 'P�rez', 'Fecha de nacimiento' => '23-11-1970', 'Tel�fonos' => array('Casa' => '4310-9030', 62 Programador PHP Experto Eugenia Bahit 'M�vil' => '15 4017- 2530', 'Trabajo' => '4604- 9000'), 'Casado' => True, 'Pasaporte' => False, ); La forma para declarar un array, es simplemente asignarlo a una variable, teniendo en cuenta, que un array, puede estar inicialmente vac�o: $mi_array = array(); Imprimir en pantalla con print_r Para imprimir una matriz completa en pantalla, se puede utilizar la funci�n print_r($array): php > $array = array(0 => 'Ana', 1 => 'Gabriela', 2 => 'Noelia',); php > print_r($array); Array ( [0] => Ana [1] => Gabriela [2] => Noelia ) php > Acceso a los �tems de un array Para acceder a un item del array, se puede realizar haciendo una llamada a su clave, o por su n�mero de �ndice (n�mero de almacenamiento interno), siendo 0 (cero) el primero: $apellidos = array('Ana' => 'Rodriguez', 'Marcos' => 'G�mez',); echo $apellidos['Ana']; // imprime Rodriguez echo $apellidos[1]; // imprime G�mez 63 Programador PHP Experto Eugenia Bahit AVISO: La sintaxis b�sica para acceder a un array es $array[indice] o $array['clave'] Los valores de un array, pueden no tener una clave expl�citamente asociada: $mi_array = array('Ana', 'Gabriela', 'Julia', 'Noelia'); En ese caso, siempre se acceder� a ellos por su n�mero de �ndice: $mi_array = array('Ana', 'Gabriela', 'Julia', 'Noelia'); echo $mi_array[2]; // imprimir� Julia Modificando elementos Para modificar un elemento, basta con acceder a �ste y asignare un nuevo valor: $mi_array = array('Ana', 'Gabriela', 'Julia', 'Noelia'); $mi_array[2] = 'Ximena'; echo $mi_array[2]; // imprimir� Ximena Agregar elementos Para agregar un valor a un array existente, se asigna �ste a un �ndice vac�o: $mi_array = array('Ana', 'Gabriela', 'Julia', 'Noelia'); $mi_array[] = 'Cecilia'; print_r($mi_array); El nuevo valor, ser� agregado al final de la lista: 64 Programador PHP Experto Eugenia Bahit php > print_r($mi_array); Array ( [0] => Ana [1] => Gabriela [2] => Julia [3] => Noelia [4] => Cecilia ) Pero si se desea asociar dicho valor a una clave, �sta debe indicarse: $telefonos_de_amigos = array('Juan' => '15 4017-2530', 'Javier' => '4921 � 1200',); $telefonos_de_amigos['Luis'] = '4321-5012'; $telefonos_de_amigos['Carlos'] = '15 3239-0432'; ADVERTENCIA sobre claves de matrices: Al usar TRUE como clave, el valor ser� evaluado al integer 1. Al usar FALSE como clave, el valor ser� evaluado al integer 0. Al usar NULL como clave, el valor ser� evaluado a un string vac�o. El uso de un string vac�o como clave, crear� (o reemplazar�) una clave con un string vac�o y su valor; no es lo mismo que usar corchetes vac�os. Vale aclarar adem�s, que si por error intentamos agregar un nuevo elemento, usando como clave o como �ndice, alguna clave o �ndice existente, estar�amos MODIFICANDO dicho elemento en vez de estar agregando uno nuevo. 65 Programador PHP Experto Eugenia Bahit Estructuras de Control � Parte II (bucles I) Recorriendo matrices din�micamente con foreach Foreach es un constructor nativo de PHP, que permite realizar operaciones iterativas (c�clicas) con matrices, recorriendo uno a uno los elementos de una matriz, comenzando por el primer elemento. Sintaxis b�sica del constructor foreach foreach($array as $valor_del_elemento) { // algoritmo a realizar con cada uno de los elementos } D�nde $array ser� el nombre de la matriz a ser iterada y $valor_del_elemento, un nombre que utilizaremos como identificador del elemento, el cual retornar� su valor: $nombres_propios = array('Ana', 'Julia', 'Luisa', 'Alberto', 'Cecilia', 'Carlos',); foreach($nombres_propios as $nombre) { echo $nombre . chr(10); } /* Salida: Ana Julia Luisa Alberto Cecilia Carlos */ 66 Programador PHP Experto Eugenia Bahit Es posible tambi�n, iterar obteniendo las claves de cada elemento, adem�s de su valor. Para ello, se utiliza la siguiente sintaxis: foreach($array as $clave => $valor) { // algoritmo a ejecutar en cada iteraci�n } Un ejemplo de iteraci�n compleja con foreach $datos_de_juan = array('Apellido' => 'P�rez', 'Fecha de nacimiento' => '23-11-1970', 'Tel�fonos' => array('Casa' => '4310-9030', 'M�vil' => '15 4017-2530', 'Trabajo' => '4604-9000'), 'Casado' => True, 'Pasaporte' => False, ); foreach($datos_de_juan as $titulo => $dato) { if(!is_array($dato)) { if($dato === True) { $dato = 'SI'; } else if ($dato === False) { $dato = 'NO'; } echo "{$titulo}: {$dato}" . chr(10); } else { foreach($dato as $tipo_telefono => $numero) { echo "Tel�fono {$tipo_telefono}: {$numero}" . chr(10); } } } /* Apellido: Prez Fecha de nacimiento: 23-11-1970 Tel�fono Casa: 4310-9030 Tel�fono M�vil: 15 4017-2530 Tel�fono Trabajo: 4604-9000 Casado: SI Pasaporte: NO */ 67 Programador PHP Experto Eugenia Bahit AVISO: La funci�n is_array($array) nos permite evaluar una variable y conocer si su tipo es "array". Devuelve TRUE si efectivamente es un array y FALSE en caso contrario. Modificando matrices din�micamente con foreach En el ejemplo anterior, eval�abamos si el dato recibido era True o False, asignando un nuevo valor a �ste (SI para True y NO para False). Pero, si tras finalizar el bucle, hici�ramos un print_r() a $datos_de_juan, el valor de la clave "casado" continuar�a siendo True, mientras que el de "Pasaporte", False: php > print_r($datos_de_juan); Array ( [Apellido] => Prez [Fecha de nacimiento] => 23-11-1970 [Telfonos] => Array ( [Casa] => 4310-9030 [Mvil] => 15 4017-2530 [Trabajo] => 4604-9000 ) [Casado] => 1 [Pasaporte] => ) Es decir, que ese dato, solo fue modificado en un �mbito local, el cual aplica solo a esa estructura de control. Pero �qu� sucede si queremos modificar globalmente los valores de los elementos de una matriz? PHP, nos facilita esa opci�n, asignando el valor por referencia. Una asignaci�n por referencia, se realiza 68 Programador PHP Experto Eugenia Bahit antecediendo el signo & al valor: $datos_de_juan = array('Apellido' => 'P�rez', 'Fecha de nacimiento' => '23-11-1970', 'Tel�fonos' => array('Casa' => '4310-9030', 'M�vil' => '15 4017-2530', 'Trabajo' => '4604-9000'), 'Casado' => True, 'Pasaporte' => False, ); foreach($datos_de_juan as $titulo => &$dato) { if(!is_array($dato)) { if($dato === True) { $dato = 'SI'; } else if ($dato === False) { $dato = 'NO'; } echo "{$titulo}: {$dato}" . chr(10); } else { foreach($dato as $tipo_telefono => $numero) { echo "Tel�fono {$tipo_telefono}: {$numero}" . chr(10); } } } Si tras el caso anterior, hici�ramos un print_r() notar�amos que los valores de "Casado" y "Pasaporte" han modificado su valor y su tipo: php > print_r($datos_de_juan); Array ( [Apellido] => Prez [Fecha de nacimiento] => 23-11-1970 [Telfonos] => Array ( [Casa] => 4310-9030 [Mvil] => 15 4017-2530 [Trabajo] => 4604-9000 ) [Casado] => SI [Pasaporte] => NO ) La asignaci�n por referencia, suele ser muy �til, cuando por ejemplo, se necesita aplicar una misma funci�n, a todos los elementos de un array (por ejemplo, convertir a may�sculas 69 Programador PHP Experto Eugenia Bahit todos los valores, con la funci�n strtoupper(), nativa de PHP): $nombres = array('Ana', 'Julia', 'Luisa', 'Alberto', 'Cecilia', 'Carlos',); foreach($nombres as &$nombre) { $nombre = strtoupper($nombre); } print_r($nombres); /* Array ( [0] => ANA [1] => JULIA [2] => LUISA [3] => ALBERTO [4] => CECILIA [5] => CARLOS ) */ 70 Programador PHP Experto Eugenia Bahit Estructuras de Control � Parte III (bucles II) While, un bucle simple As� como foreach puede parecer uno de los bucles m�s complejos, while, resulta ser el m�s simple de todos. While, simplemente evaluar� de forma booleana (true o false) una expresi�n de iterativamente, hasta que la expresi�n evaluada retorne False, y parar�. Su sintaxis es la siguiente: while (expresi�n) { // algoritmo a ejecutar hasta expresi�n retorne False } O dicho de forma humanamente legible: mientras que (esta condici�n se cumpla) { hacer esto } Un ejemplo sencillo $n = 0; while ($n <= 5) { echo $n . chr(10); $n++; // incremento el valor de $n en 1. Equivale a $n = $ + 1; } Lectura humana: iniciar N en cero 71 Programador PHP Experto Eugenia Bahit mientras que (N sea menor o igual a 5) { imprimir N incrementar N } Un ejemplo pr�ctico $years = array(); $year = 1990; while ($year <= 2000) { $years[] = $year; $year++; } print_r($years); /* Array ( [0] => 1990 [1] => 1991 [2] => 1992 [3] => 1993 [4] => 1994 [5] => 1995 [6] => 1996 [7] => 1997 [8] => 1998 [9] => 1999 [10] => 2000 ) */ Vale la pena hacer notar, que si al iniciar una iteraci�n con while, la primera expresi�n es falsa, no se continuar� ejecutando el bucle: $years = array(); $year = 1990; while ($year < 1990) { $years[] = $year; $year++; } print_r($years); /* 72 Programador PHP Experto Eugenia Bahit Array () */ Do while, tan simple como while pero con una ventaja El bucle do-while es, como indica el t�tulo, tan simple como while y funciona de manera pr�cticamente id�ntica. La �nica diferencia, el algoritmo iterativo se ejecutar� s� o s�, una vez al comienzo y luego, evaluar� la expresi�n, y volver� a ejecutar el algoritmo si la expresi�n es verdadera. Su sintaxis es la siguiente: do { /* algoritmo a ejecutarse al principio de la iteraci�n y toda vez que expresi�n sea verdadera */ } while ($expresion); Lectura humana: hacer { esto, la primera y vez y luego hacerlo... } mientras que (esta condici�n se cumpla); Un ejemplo simple $years = array(); $year = 1990; do { $years[] = $year; $year++; } while ($year < 1990); 73 Programador PHP Experto Eugenia Bahit print_r($years); /* Array ( [0] => 1990 ) */ print $year; // 1991 74 Programador PHP Experto Eugenia Bahit Bucles for, los m�s complejos de PHP Los bucles for son los ciclos iterativos m�s complejos de los cu�les se dispone en PHP. �stos, eval�an 3 expresiones en cada iteraci�n, siguiendo este esquema: � Primera expresi�n: se ejecuta incondicionalmente al comienzo del bucle; � Segunda expresi�n: se eval�a como verdadera o falsa al inicio de cada iteraci�n. El bucle contin�a, solo si es verdadera. � Tercera expresi�n: se ejecuta al final de cada iteraci�n Sintaxis: for (expresion1; expresion2; expresion3) { // algoritmo que se ejecuta cuando expresion2 es verdadera } Un ejemplo: for ($i = 0; $i <= 3; $i++) { echo $i . chr(10); } /* 0 12 3 */ Cada una de las expresiones, puede contener m�ltiples expresiones, las cuales deber�n ir separadas por una coma: 75 Programador PHP Experto Eugenia Bahit for ($i = 1, $prefijo = "Codigo N� 000"; $i <= 4; $sufijo = chr(10), $i++) { echo "{$prefijo}{$i}{$sufijo}"; } /* Codigo N� 0001 Codigo N� 0002 Codigo N� 0003 Codigo N� 0004 */ AVISO: Al utilizar expresiones m�ltiples, se debe tener en cuenta, que las expresiones m�ltiples en la segunda expresi�n son evaluadas, pero el resultado solo se toma de la �ltima parte. Un ejemplo absurdo, pero que grafica el aviso anterior: for ($i = 1, $prefijo = "Codigo N� 000"; $i <= 4, $i < 2; $sufijo = chr(10), $i++) { echo "{$prefijo}{$i}{$sufijo}"; } /* Codigo N� 0001 */ No obstante, es posible que puedan coexistir expresiones vac�as: for ($i = 1, $prefijo = "Codigo N� 000", $sufijo = chr(10); ; $i++) { if ($i > 4) { break; } echo "{$prefijo}{$i}{$sufijo}"; } /* Codigo N� 0001 Codigo N� 0002 Codigo N� 0003 Codigo N� 0004 76 Programador PHP Experto Eugenia Bahit */ AVISO: Si la segunda expresi�n est� vac�a, el bucle ser� corrido de forma indefinida, motivo por el cual, debe utilizarse en alg�n punto, la expresi�n break (vista en switch). De lo contrario, podr�a producirse un desbordamiento de la memoria. Esto, es debido a que PHP, considera la expresi�n vac�a como True (al igual que C). 77 Programador PHP Experto Eugenia Bahit Curiosidades sint�cticas de la bipolaridad no diagnosticada de PHP Queremos a PHP y por eso lo usamos �cierto? Pero aquello de que "el amor es ciego", es bastante relativo, cuando lo que nos gu�a como programadores, es la l�gica. Y es all�, cuando nos encontramos con que PHP, pareciera ser "bipolar" y, seg�n su �nimo, la sintaxis de las estructuras de control, pueden escribirse de dos formas: la que ya conocemos y... esta de aqu�: if($PHP == "es bipolar"): echo "No te aconsejo utilizar esta sintaxis :D"; endif; Y a veces, est� un poco deprimido: if($PHP == "es bipolar"): echo "No te aconsejo utilizar esta sintaxis :D"; else if ($PHP == "est� en su fase depresiva"): echo "Oh! No! Debiste utilizar elseif sin separaci�n! Ahora fallar�!"; endif; Por curiosidad, el ejemplo anterior pero sin fallas: if($PHP == "es bipolar"): echo "No te aconsejo utilizar esta sintaxis :D"; elseif ($PHP == "est� en su fase depresiva"): echo "Ok. Usemos elseif sin separaci�n as� no se enoja"; else: echo "Ya podemos volver a utilizar llaves"; endif; �Oh, no! Con while tambi�n sucede! while ($i < 10): echo "Y aqu� tenemos un PHP man�aco!"; endwhile; 78 Programador PHP Experto Eugenia Bahit Y tal cual lo imaginas, efectivamente, con for y foreach, PHP puede ser �bipolar�. �No me crees? Mira esto! for ($i = 0; $i < 3; $i++): echo $i . chr(10); endfor; $array = array(1, 2, 3); foreach($array as $a): echo $a . chr(10); endforeach; Goto, si lo usas... es tu elecci�n! Si todo lo anterior, te dej� la frase �no quer�a saber tanto�, rondando en tu cabeza, sabr�s disculparme, pero debo contarte tambi�n sobre goto (aunque entre nos, a veces puede ser �til). goto es un �operador� al que podr�amos llamar �operador sint�ctico�, que se utiliza para �saltar� a una zona espec�fica del programa. El punto de destino es especificado mediante una etiqueta seguida de dos puntos y la instrucci�n es dada como goto seguida de la etiqueta del destino deseado. Goto podr�a reemplazar en ocasiones, el uso de break, puesto que solo puede ser utilizado dentro de una misma estructura y con varias restricciones. Un ejemplo no-pr�ctico para entender goto <?php echo "Hola Mundo!"; goto mi_etiqueta; echo "Esto no se mostrar�, ya que goto lo saltar�"; mi_etiqueta: 79 Programador PHP Experto Eugenia Bahit echo chr(10) . "Esto s� ser� mostrado" . chr(10); echo "Y esta tambi�n!" . chr(10); ?> Lo anterior, generar� la siguiente salida: eugenia@cocochito:~/cursophpbasico$ php -f file.php Hola Mundo! Esto s� ser� mostrado Y esta tambi�n! eugenia@cocochito:~/cursophpbasico$ Sin embargo, al no tener las etiquetas definidas, un identificador de cierre y, PHP, ser un lenguaje que no requiere de identaci�n para la definici�n de estructuras, todo el c�digo escrito debajo de una etiqueta, ser� ejecutado cuando goto sea llamado: <?php echo "Hola Mundo!"; goto mi_etiqueta; echo "Esto no se mostrar�, ya que goto lo saltar�"; mi_etiqueta: echo chr(10) . "Esto s� ser� mostrado" . chr(10); otra_etiqueta: echo "mmmm... esta tambi�n se muestra :/" . chr(10); ?> Y aqu�, la prueba: eugenia@cocochito:~/cursophpbasico$ php -f file.php Hola Mundo! Esto s� ser� mostrado mmmm... esta tambi�n se muestra :/ eugenia@cocochito:~/cursophpbasico$ 80 Programador PHP Experto Eugenia Bahit Por lo tanto, utilizar goto, puede ser engorroso ya que para evitar lo anterior, deber�an especificarse m�s y m�s etiquetas y m�s y m�s gotos: <?php echo "Hola Mundo!"; goto mi_etiqueta; echo "Esto no se mostrar�, ya que goto lo saltar�"; mi_etiqueta: echo chr(10) . "Esto s� ser� mostrado" . chr(10); goto ya_basta_de_ejecutarse; otra_etiqueta: echo "Ahora ya no voy a imprimirme!" . chr(10); ya_basta_de_ejecutarse: exit(); ?> Como se ve a continuaci�n, esta vez, lo logramos: eugenia@cocochito:~/cursophpbasico$ php -f file.php Hola Mundo! Esto s� ser� mostrado eugenia@cocochito:~/cursophpbasico$ Puedes ver m�s sobre goto, leyendo la documentaci�n oficial: http://www.php.net/manual/es/control-structures.goto.php 81 Programador PHP Experto Eugenia Bahit Funciones definidas por el usuario Definici�n Una funci�n, es una forma de agrupar expresiones y sentencias (algoritmos) que realicen determinadas acciones, pero que �stas, solo se ejecuten cuando son llamadas (al igual que las funciones nativas de PHP). Declarando Funciones Sintaxis b�sica La sintaxis b�sica de una funci�n, es verdaderamente sencilla: function nombre_de_la_funcion(parametros) { // algoritmo } Sobre el nombre de las funciones Para el nombre de las funciones, aplica todo lo dicho para el nombre de variables. Sobre los par�metros Un par�metro es un valor, que la funci�n espera a fin de ejecutar acciones en base al mismo. Una funci�n puede esperar uno o m�s par�metros (que ir�n separados por una coma) o ninguno. 82 Programador PHP Experto Eugenia Bahit function nombre_de_la_funcion(parametro1, parametro2) { // algoritmo } function otra_funcion() { // algoritmo } Los par�metros, se indican entre los par�ntesis, a modo de variables, a fin de poder utilizarlos como tal, dentro de la misma funci�n: function nombre_de_la_funcion($parametro1, $parametro2) { // algoritmo } Adem�s, a cada par�metro, se le puede asignar un valor por defecto de cualquier tipo: function nombre_de_la_funcion($nombre, $edad=25, $sexo='F') { // algoritmo } Llamando a una funci�n Una funci�n no ser� ejecutada nunca, hasta que no se la llame: <?php function hola_mundo() { echo "Hola Mundo!" . chr(10); } ?> Ninguna salida ser� obtenida de lo anterior, puesto que la funci�n, no ha sido llamada: eugenia@cocochito:~$ php -f file.php eugenia@cocochito:~$ 83 Programador PHP Experto Eugenia Bahit Para llamar a una funci�n, simplemente debe especificarse su nombre, en el lugar preciso donde se desea que �sta se ejecute. Siempre deben incluirse los par�ntesis en las llamadas, incluso aunque no requieran que un par�metro les sea pasado: <?php function hola_mundo() { echo "Hola Mundo!" . chr(10); } hola_mundo(); ?> Ahora s�, se obtendr� el resultado de la ejecuci�n de la funci�n: eugenia@cocochito:~$ php -f file.php Hola Mundo! eugenia@cocochito:~$ Sobre la finalidad de las funciones Una funci�n, puede tener cualquier tipo de algoritmo y cualquier cantidad de ellos y, utilizar cualquiera de las caracter�sticas vistas hasta ahora. No obstante ello, una buena pr�ctica, indica que la finalidad de una funci�n, debe ser realizar una �nica acci�n, reutilizable y por lo tanto, tan gen�rica como sea posible. Paso de variables por referencia en funciones Al igual que en el bucle foreach, es posible pasar variables por referencia a una funci�n. 84 Programador PHP Experto Eugenia Bahit Para ello, al definirse la funci�n, debe colocarse el signo �&� antecediendo a aquellos par�metros que hagan referencia a una variable global: // definimos una variable de �mbito global $mi_variable_global = 10; // definimos una funci�n que modificar� la variable global function modificar_variable_global(&$variable, $otro_parametro) { $variable = $variable * $otro_parametro; } // llamamos a la funci�n pasando como referencia la variable global modificar_variable_global($mi_variable_global, 2); // imprimimos la variable global echo $mi_variable_global; // salida: 20 Modificando variables globales mediante el uso de global En PHP, es posible tambi�n, modificar una variable de �mbito global, sin necesidad de pasarla como referencia. Para ello, dentro de la funci�n, se har� referencia a la variable global a ser utilizada, mediante el uso de la palabra clave �global�. El siguiente ejemplo, es sin�nimo del anterior: // definimos una variable de �mbito global $mi_variable_global = 10; // definimos la funci�n que har� referencia a la variable global function modificar_variable_global($otro_parametro) { global $mi_variable_global; $mi_variable_global = $mi_variable_global * $otro_parametro; } // llamamos a la funci�n modificar_variable_global(2); // imprimimos la variable global echo $mi_variable_global; // salida: 20 85 Programador PHP Experto Eugenia Bahit AVISO N�tese que si la variable global a la cual se hace referencia dentro de la funci�n, no ha sido declarada previamente, global $mi_variable crear� la variable global $mi_variable. Llamadas de retorno En PHP, es posible (al igual que en la gran mayor�a de los lenguajes de programaci�n), llamar a una funci�n dentro de otra, de forma fija y de la misma manera que se la llamar�a, desde fuera de dicha funci�n: function mi_funcion($parametro) { mi_otra_funcion(); $una_variable = otra_funcion_mas($parametro); } Sin embargo, es posible que se desee realizar dicha llamada, de manera din�mica, es decir, desconociendo el nombre de la funci�n a la que se desear� llamar. A este tipo de acciones, se las denomina llamadas de retorno. En una llamada de retorno, el nombre de la funci�n a la cual se desea llamar, es pasado como una cadena de texto y para ello, se utiliza la funci�n nativa de PHP, call_user_func('nombre_de_la_funcion_a_llamar'). // Funci�n que llamar� desde otra funci�n function decir_hola() { return "Hola Mundo!"; } // Funci�n que har� la llamada de retorno function llamar_a_otra($funcion) { echo call_user_func($funcion); echo chr(10); // continuaci�n del algoritmo 86 Programador PHP Experto Eugenia Bahit } llamar_a_otra('decir_hola'); Pasar argumentos en una llamada de retorno �Qu� sucede si la funci�n a la cual se desea llamar, necesita recibir uno o m�s argumentos? En este caso, tenemos dos opciones: 1) Pasar los argumentos a continuaci�n del nombre de la funci�n: call_user_func('nombre_de_la_funcion', $parametro1, $parametro2); Ejemplo: // Funci�n que llamar� con call_user_func function sumar_dos_numeros($a, $b) { return $a + $b; } $numero_1 = 5; $numero_2 = 10; $resultado = call_user_func('sumar_dos_numeros', $numero_1, $numero_2); echo $resultado; 2) Definir un array con todos los argumentos necesarios, y hacer la llamada de retorno ampliada: call_user_func_array('nombre_de_la_funcion', $array_con_argumentos); Ejemplo: // Funci�n que llamar� con call_user_func_array function sumar_dos_numeros($a, $b) { 87 Programador PHP Experto Eugenia Bahit return $a + $b; } $args = array(5, 10); $resultado = call_user_func_array('sumar_dos_numeros', $args); echo $resultado; Argumentos no conocidos Cuando dentro de una funci�n, realizamos una llamada de retorno, as� como la funci�n puede desconocer el nombre de aquella a la cual deber� llamar, tambi�n es probable que si esa funci�n requiere de argumentos, se desconozca tambi�n la cantidad de argumentos. Para resolver este planteo, contamos con varias funciones nativas que nos ayudar�n a lograr un mejor tratamiento de las llamadas de retornos. Veamos algunas de ellas. Conocer la cantidad de argumentos Con la func_num_args podemos conocer exactamente, la cantidad de argumentos recibidos en una funci�n: function foo() { $cantidad_de_argumentos = func_num_args(); echo "Recibimos {$cantidad_de_argumentos} argumentos"; } foo('argumento 1', 'otro_argumento'); Como podemos notar, la funci�n foo() en realidad, no esperaba ning�n argumento. Sin embargo, al hacer la llamada a foo(), hemos pasado dos argumentos. 88 Programador PHP Experto Eugenia Bahit Obtener una lista completa de todos los argumentos Es posible obtener una matriz (array) con todos los argumentos recibidos. Para ello, disponemos de la funci�n func_get_args: function foo() { $argumentos = func_get_args(); print_r($argumentos); /* Retornar� un array con todos los argumentos: Array ( [0] => argumento 1 [1] => otro argumento ) */ } foo('argumento 1', 'otro argumento'); Obtener un argumento espec�fico Puede ser muy util adem�s, obtener un argumento determinado. Para ello, disponemos de la funci�n func_get_arg(index), donde index, ser� el n�mero de �ndice del argumento en la matriz: function foo3() { echo func_get_arg(1); // salida: otro argumento } foo3('argumento 1', 'otro argumento'); Saber si una funci�n puede ser llamada (callable) Cuando decimos callable nos referimos a si la funci�n existe y adem�s, puede ser llamada. 89 Programador PHP Experto Eugenia Bahit Cuando trabajamos con llamadas de retorno, asumimos que nuestro script, desconoce el nombre de la funci�n a la que se desea llamar y por lo tanto, no debemos confiar en que el nombre de la funci�n pasada como cadena de texto, sea efectivamente el nombre de una funci�n callable. Para sortear este obst�culo, disponemos de la funci�n is_callable, la cual nos retornor� TRUE en caso de ser una funci�n calleable. De lo contrario, retornar� FALSE. function funcion_callable() { echo "Llamada correcta"; } function llamar_a_funcion_callable($funcion) { if(is_callable($funcion)) { call_user_func($funcion); } else { echo "La funci�n no es callable"; } } llamar_a_funcion_callable('funcion_callable'); // salida: Llamada correcta llamar_a_funcion_callable('funcion_inexistente'); // salida: La funci�n no es calleable Material de lectura adicional � Sobre call_user_func http://www.php.net/manual/es/function.call-user-func.php � Sobre call_user_func_array http://www.php.net/manual/es/function.call-user-funcarray. php � Sobre func_num_args 90 Programador PHP Experto Eugenia Bahit http://www.php.net/manual/es/function.func-numargs. php � Sobre func_get_args http://www.php.net/manual/es/function.func-get-args.php � Sobre func_get_arg http://www.php.net/manual/es/function.func-get-arg.php � Sobre is_callable http://www.php.net/manual/es/function.is-callable.php � Sobre function_exists (alternativa a is_callable que solo comprueba si la funci�n existe o no, pero no verifica que �sta, pueda ser llamada) http://www.php.net/manual/es/function.functionexists. php Diferentes formas de recoger argumentos para hacer una llamada de retorno Veremos aqu�, dos formas de crear funciones para hacer llamadas de retorno que requieran de argumentos. Forma 1: recibir argumentos en un array // funci�n callable function callable_func_1($arg1, $arg2, $arg3) { $result = ($arg1 + $arg2) * $arg3; return $result; } // funci�n que har� la llamada de retorno function forma_1($funcion, $argumentos=array()) { $result = NULL; if(is_callable($funcion)) { $result = call_user_func_array($funcion, $argumentos); } 91 Programador PHP Experto Eugenia Bahit return $result; } // implemenatci�n $args = array(10, 5, 2); $resultado = forma_1('callable_func_1', $args); echo $resultado; Forma 2: recibir argumentos 1 a 1 // funci�n callable function callable_func_1($arg1, $arg2, $arg3) { $result = ($arg1 + $arg2) * $arg3; return $result; } // funci�n que har� la llamada de retorno function forma_2() { $num_args = func_num_args(); $args = func_get_args(); $result = NULL; // verifico que al menos se reciba 1 argumento if($num_args >= 1) { // obtengo el nombre de la funci�n (asumo que es el 1er arg.) $funcion = func_get_arg(0); // elimino el nombre de la funci�n de los argumentos array_shift($args); // elimino el �ndice 0 // verifico que sea una funci�n callable y la llamo if(is_callable($funcion)) { $result = call_user_func_array($funcion, $args); } } return $result; } // implementaci�n $funcion = 'callable_func_1'; $arg1 = 10; $arg2 = 5; $arg3 = 2; $resultado = forma_2($funcion, $arg1, $arg2, $arg3); echo $resultado; 92 Programador PHP Experto Eugenia Bahit Llamadas recursivas Se denomina llamada recursiva (o recursividad), a aquellas funciones que en su algoritmo, hacen referencia s� misma. Las llamadas recursivas suelen ser muy �tiles en casos muy puntuales, pero debido a su gran factibilidad de caer en iteraciones infinitas, deben extremarse las medidas precautivas necesarias y solo utilizarse cuando sea estrictamente necesario y no exista una forma alternativa viable, que resuelva el problema, evitando la recursividad. PHP admite las llamadas recursivas, permitiendo a una funci�n, llamarse a s� misma, de igual forma que lo hace cuando llama a otra funci�n. function funcion_recursiva() { //algoritmo... funcion_recursiva(); } Veamos como funciona. En el siguiente ejemplo: function funcion_recursiva($a=0) { if($a == 0) { $a = 1; $a = funcion_recursiva($a); } else { $a = $a*2; } return $a; } si llamo a funcion_recursiva() sin pasar ning�n par�metro, funcion_recursiva tomar� el valor de $a definido por defecto (0). Ejecutar� entonces el if, se llamar� a si misma, y en esta segunda ejecuci�n, actuar� el else. si en cambio, llamara a funcion_recursiva(5) pasando un entero como par�metro, se ejecutar� el else directamente. Lo 93 Programador PHP Experto Eugenia Bahit mismo suceder�a si en vez de un entero, pasara una cadena como par�metro. 94 Programador PHP Experto Eugenia Bahit Helpers En programaci�n, un helper es una funci�n o conjunto de funciones gen�ricas, de uso com�n, destinadas a servir de ayuda a otros procesos dentro de un mismo sistema. Un helper que retorna la fecha actual /* Retorna la fecha actual en formato largo, corto o ISO (can�nico) Argumentos: $formato -- largo, retorna la fecha actual en formato Lunes, 2 de Agosto de 2011 corto, retorna la fecha en formato 02/08/2011 ISO, retorna la fecha en formaro 2011-08-02 */ function get_fecha_actual($formato) { // defino un array con los patrones de formato $formato_fecha = array( "largo" => "l, j \d\e F \d\e Y", "corto" => "d/m/Y", "ISO" => "Y-m-d", ); // inicializo la variable $fecha $fecha = NULL; // compruebo que $formato sea un formato v�lido if(array_key_exists($formato, $formato_fecha)) { // si el formato es v�lido, reasigno el valor a $fecha $fecha = date($formato_fecha[$formato]); } // retorno la fecha formateada return $fecha; } 95 Programador PHP Experto Eugenia Bahit Un helper que modifica una variable global, haciendo una llamada de retorno /* Llama a la funci�n indicada y reasigna el valor de una variable global, formateado por la funci�n indicada Argumentos: $variable -- variable global a ser modificada $funcion -- funci�n a la cual debe llamarse para dar formato a $variable $argumentos -- (opcional) par�metros que eventualmente puedan ser requeridos por $funcion */ function set_variable_global(&$variable, $funcion, $argumentos=array()) { // compruebo que $funcion sea una funci�n callable if(is_callable($funcion)) { $variable = call_user_func_array($funcion, $argumentos); } } 96 Programador PHP Experto Eugenia Bahit Taller de Funciones En este taller, veremos como, utilizando buenas pr�cticas de programaci�n, nos ayudaremos de funciones definidas por el usuario, para lograr: � Un sistema din�mico, seguro, f�cilmente mantenible y escalable � Lograr una completa abstracci�n de c�digo HTML, evitando embeberlo y/o fusionarlo con PHP Archivos necesarios para el taller Descarga los archivos que utilizaremos en el taller, pulsando el siguiente enlace: http://taller-de-php.eugeniabahit.com/taller-defunciones. tar.gz 97 Programador PHP Experto Eugenia Bahit Trabajando con el Sistema de Archivos PHP dispone de un conjunto de funciones nativas, que nos permiten trabajar holgadamente todo el sistema de archivos, como si lo manej�remos desde el propio sistema operativo. En este cap�tulo, veremos las principales funciones del sistema de archivos, que nos ser� �tiles para la mayor parte de las aplicaciones que requieran manipular tanto archivos como directorios. Recorrido r�pido por las principales funciones De forma r�pida, veremos aqu� como abrir archivos, leer su contenido, manipularlo, crear archivos y escribir en ellos. Comencemos! Apertura de archivos Sin dudas, cuando solo se requiere abrir un archivo para leer su contenido, el modo m�s pr�ctico de hacerlo es con la funci�n file_get_contents vista al comienzo (y a lo largo) de este curso. No obstante, PHP dispone de la funci�n fopen() que permite, no solo abrir el archivo para leerlo, sino tambi�n, para escribir en �l y manipular sus datos. fopen($archivo, $modo[, $include_path]); 98 Programador PHP Experto Eugenia Bahit AVISO: Los corchetes [ y ] indican que el par�metro es opcional. fopen abrir� el archivo en el modo indicado y creando un puntero en el mismo. Puntero: lugar del el archivo en el cual se coloca el cursor al ser abierto. Toda vez que un archivo sea abierto, debe cerrarse a fin de liberarlo de la memoria. Para ello, utilizamos fclose($cursor_creado_con_fopen): $cursor = fopen('archivo.txt', 'r'); fclose($cursor); Modos de apertura Los modos posibles de apertura, los siguientes: Modo Descripci�n Puntero r Lectura Al inicio del archivo r+ Lectura y escritura Al inicio del archivo w Escritura Si e archivo no existe, intenta crearlo. Si existe, lo sobreescribe. Al inicio del archivo w+ Lectura y escritura Si e archivo no existe, intenta crearlo. Si existe, lo sobreescribe. Al inicio del archivo, trunc�ndolo a Escritura Si el archivo no existe, intenta crearlo. Al final del archivo a+ Lectura y escritura Si e archivo no existe, intenta crearlo. Al final del archivo x Escritura Crea un nuevo archivo para escribir en �l. Si el archivo ya existe, falla. Al inicio del archivo x+ Lectura y Escritura Al inicio del archivo 99 Programador PHP Experto Eugenia Bahit Crea un nuevo archivo para escribir en �l y luego poder leerlo. Si el archivo ya existe, falla. c Escritura Si e archivo no existe, intenta crearlo. Al inicio del archivo c+ Lectura y escritura Si e archivo no existe, intenta crearlo. Al inicio del archivo Ruta hacia el archivo La ruta especificada hacia el archivo, debe seguir la forma protocolo://ruta_al_archivo. De esta forma, si quisi�ramos abrir una URL, deber�amos utilizar: http://www.dominio.com/archivo.txt Pero si quisi�ramos abrirlo localmente, deber�amos indicar: /ruta_a_mi_dominio/public_html/archivo.txt ADVERTENCIA N�tese que en Windows deber� utilizarse el siguiente formato: c:\\ruta_a\\archivo.txt Utilizar o no include_path El tercer par�metro (opcional), permite indicar si se desea buscar el archivo en el include_path seteado en el archivo php.ini. En dicho caso, debe pasarse TRUE (o 1): $file = fopen('file.txt', 'r', TRUE); Generalmente, no utilizaremos este par�metro, a no ser que sea extrictamente necesario. Lectura de Archivos Una vez abierto un archivo, podremos leer su contenido 100 Programador PHP Experto Eugenia Bahit utilizando la funci�n de lectura en modo binario seguro, fread(). Utilizando la siguiente sintaxis: fread($recurso, $bytes); podremos leer el contenido de un archivo en modo binario seguro, necesitando de un recurso (obtenido mediante fopen) e indicando la cantidad de bytes a leer (1 car�cter = 1 byte). Para leer el contenido completo del archivo, podemos ayudarnos de la funci�n filesize($archivo), donde $archivo, ser� la ruta completa al archivo que se quiere leer: $archivo = "archivo.txt"; // nombre del archivo $bytes = filesize($archivo); // tama�o del archivo $cursor = fopen($archivo, "r"); // abrir archivo $contenido = fread($cursor, $bytes); // leer contenido fclose($cursor); // cerrar el cursor (liberar memoria) Escribir en un archivo Para escribir en un archivo, nos valemos de la funci�n fwrite() la cu�l escribir� en modo binario seguro. Su sintaxis es la siguiente: fwrite($recurso, $contenido_a_escribir[, $cantidad_de_bytes_a_escribir]); El contenido, puede ser cualquier variable de tipo string, mientras que la cantidad de bytes a escribir, es opcional. Si se indica la cantidad de bytes, se dejar� de escribir cuando la cantidad de bytes se haya alcanzado o cuando la cadena termine (lo que suceda primero). 101 Programador PHP Experto Eugenia Bahit $archivo = "archivo.txt"; $recurso = fopen($archivo, "a+"); $nuevo_contenido = "nuevo contenido"; fwrite($recurso, $nuevo_contenido); $bytes = filesize($archivo); $contenido = fread($recurso, $bytes); fclose($recurso); Moviendo el puntero dentro del archivo Cuando tenemos que escribir un archivo, es muy �til saber en qu� lugar se encuentra el puntero, y moverlo a la posici�n indicada. Podemos obtener la posici�n actual del puntero, con la funci�n ftell($recurso) y movernos hacia el byte indicado, con fseek($recurso, $byte). Un contador de visitas sencillo function contador_de_visitas() { $archivo = "contador.txt"; $recurso = fopen($archivo, "r+"); $bytes_totales = filesize($archivo); $contador = fread($recurso, $bytes_totales); $nuevo_contenido = $contador + 1; $posicion_actual = ftell($recurso); if($posicion_actual == $bytes_totales) { // me muevo al byte 0 para sobreescribir el archivo fseek($recurso, 0); } fwrite($recurso, $nuevo_contenido); fclose($recurso); return $nuevo_contenido; } // Actualizar el n�mero de visitas y mostrarlo echo contador_de_visitas(); 102 Programador PHP Experto Eugenia Bahit �Cuidado con los permisos! Como es l�gico de esperar, para poder crear un archivo o escribir sobre un archivo existente, �ste, debe tener permisos de escritura para el usuario www-data. En un servidor Web, tener archivos o directorios servidos con permisos de escritura, es una puerta que se est� abriendo hasta para el m�s novato de los delincuentes inform�ticos que transitan por la red. La mejor alternativa, es tener un directorio NO SERVIDO con permisos de escritura (es decir, un directorio con permisos de escritura, fuera del directorio de publicaci�n Web). En este caso, bastar� con utilizar como ruta del archivo, la ruta absoluta. Trabajando con directorios Como hemos comentado antes, PHP permite trabajar el sistema de archivos, como podr�amos hacerlo desde las aplicaciones del propio sistema operativo. Y esto, incluye tambi�n, funciones relacionadas a los directorios. Creando el gestor Al igual que con los archivos, para acceder a un directorio, debe crearse primero un recurso (gestor de directorio). Para ello, al igual que fopen abre un archivo, tenemos una funci�n para abrir los directorios. Tal vez, tomando como base l�gica el nombre de la funci�n fopen (que proviene de FileOpen), est�s esperando una funci�n llamada dopen, pero lamentablemente, no existe una funci�n llamada dopen, ya que PHP, no tiene estandarizado el estilo para nombres de funciones. A diferencia de lo que esperamos, para abrir un directorio, la funci�n que debemos utilizar, se denomina opendir. 103 Programador PHP Experto Eugenia Bahit $recurso = opendir('nombre_del_directorio'); Como nombre de directorio, es posible utilizar tambi�n, cualquier ruta absoluta: $recurso = opendir('/var/www/dominio.com/public_html/archivos/pdf'); o una ruta relativa: $recurso = opendir('../archivos/pdf'); $otro_recurso = opendir('archivos/pdf'); Al igual que cuando abrimos un archivo, cuando abrimos un directorio, es necesario cerrarlo para liberarlo de memoria: closedir($recurso); Explorando el contenido de un directorio Explorar el contenido de un directorio, es sumamente sencillo, ya que disponemos de una funci�n para hacerlo: readdir($recurso). Sin embargo, la exploraci�n de directorios puede ser compleja, debido a que: � readdir no devuelve el contenido completo de un directorio en su primera ejecuci�n, sino que va leyendo cada elemento de a uno por vez y por lo tanto, readdir debe ejecutarse iterativamente; � readdir retorna el nombre del elemento (archivo o directorio) pero en caso de error, puede devolver tanto False como un valor no booleano que pueda ser evaluado como False, por lo cual, antes de ejecutar alguna acci�n, debe verificarse el retorno. � En sistemas basados en UNIX, todo directorio contiene a 104 Programador PHP Experto Eugenia Bahit la vez dos subdirectorios ocultos cuyos nombres son . (punto) y .. (doble punto), que deben ser validados previamente a fin de evitar listarlos. // abro el directorio $dir = opendir('../taller-de-funciones'); // itero solo si readdir NO devuelve False while(($elemento = readdir($dir)) !== False) { // imprimo el nombre del archivo o directorio echo $elemento . chr(10); } // cierro el directorio closedir($dir); /* Salida: index.php template.html funciones.php .. files . */ N�tese que en el ejemplo anterior, se est�n listando los dos directorios ocultos t�picos de todo sistema UNIX-Like. Para evitar eso, ser� necesario filtrarlos: // abro el directorio $dir = opendir('../taller-de-funciones'); // inicializo un array donde guardar� cada elemento $contenido = array(); // itero solo si readdir NO devuelve False while(($elemento = readdir($dir)) !== False) { // evito que liste los directorios ocultos . y .. if($elemento != "." and $elemento != "..") { // agrego cada elemento en el array $contenido $contenido[] = $elemento; } } // cierro el directorio closedir($dir); 105 Programador PHP Experto Eugenia Bahit // imprimo la salida print_r($contenido); /* Salida: Array ( [0] => index.php [1] => template.html [2] => funciones.php [3] => files ) */ Filtrando el tipo de elemento Como vimos en el ejemplo anterior, readdir retorna tanto archivos como directorios. Es posible filtrar el tipo de elemento, para poder manipularlos de forma m�s apropiada. Para ello disponemos de cuatro funciones muy �tiles: is_dir($elemento) Nos indica si el elemento evaluado es un directorio (True) o no (False) is_file($elemento) Nos indica si el elemento evaluado es un archivo (True) o no (False). is_link($elemento) Nos indica si el elemento evaluado es un elace simb�lico (True) o no (False). N�tese que en Windows, los enlaces simb�licos son denominados �accesos directos�. 106 Programador PHP Experto Eugenia Bahit filetype($elemento) Nos retorna el tipo de elemento siendo los valores de retorno posibles: fifo, char, dir, block, link, file, socket y unknown. ADVERTENCIA N�tese que filetype podr� devolver False si no pudo ejecutarse con �xito pero tambi�n podr�a devolver un error, si el tipo de archivo es desconocido. $dir = opendir('../taller-de-funciones'); $archivos = array(); $directorios = array(); $symlinks = array(); while(($elemento = readdir($dir)) !== False) { if($elemento != "." and $elemento != "..") { $path_elemento = "../taller-de-funciones/{$elemento}"; if(is_dir($path_elemento)) { $directorios[] = $elemento; } elseif(is_file($path_elemento)) { $archivos[] = $elemento; } elseif(is_link($path_elemento)) { $symlinks[] = $elemento; } } } closedir($dir); $contenido = array('Directorios' => $directorios, 'Archivos' => $archivos, 'Enlaces simb�licos' => $symlinks); print_r($contenido); Lo anterior, producir� la siguiente salida: eugenia@cocochito:~/borradores$ php -f file.php Array ( 107 Programador PHP Experto Eugenia Bahit [Directorios] => Array ( [0] => files ) [Archivos] => Array ( [0] => index.php [1] => template.html [2] => funciones.php ) [Enlaces simb�licos] => Array ( ) ) N�tese que alternativamente a las tres funciones utilizadas en el ejemplo (is_file, is_dir e is_link) se podr�a comprobar mediante filetype($path_elemento). Sin embargo, la forma segura de checkear el tipo de elemento, es con las funciones usadas en el c�digo anterior. Otras funciones que necesitar�s con frecuencia Muchas veces, ser� necesario saber si el directorio o archivos que intentamos abrir, existe, conocer si puede ser le�do y/o escrito. Estas acciones ser�n muy frecuentes, y para resolver el dilema, disponemos de las funciones necesarias. Comprobar la existencia de un archivo o directorio file_exists('archivo_o_directorio') Comprueba si un archivo o directorio existe (True) o no (False): 108 Programador PHP Experto Eugenia Bahit // validando si un archivo existe $archivo = 'ruta_a/mi_archivo.txt'; if(file_exists($archivo)) { echo "El archivo {$archivo} existe"; } else { echo "El archivo {$archivo} no pudo localizarse"; } // ahora, verificando si un directorio existe $directorio = 'ruta/a/mi/carpeta'; if(file_exists($directorio)) { echo "El directorio {$directorio} existe"; } else { echo "El directorio {$directorio} no pudo localizarse"; } Comprobar si un archivo o directorio es legible is_readable('archivo_o_directorio') Comprueba si un archivo o directorio es legible (True) o no (False): // validando si un archivo es legibe $archivo = 'ruta_a/mi_archivo.txt'; if(is_readable($archivo)) { echo "El archivo {$archivo} puede ser le�do"; } else { echo "El archivo {$archivo} no puede ser le�do"; } // ahora, verificando si un directorio es legible $directorio = 'ruta/a/mi/carpeta'; if(is_readable($directorio)) { echo "El directorio {$directorio} puede ser le�do"; } else { echo "El directorio {$directorio} no puede ser le�do"; } 109 Programador PHP Experto Eugenia Bahit Comprobar si un archivo o directorio puede escribirse is_writable('archivo_o_directorio') Comprueba si un archivo o directorio es legible (True) o no (False): // validando si un archivo puede escribirse $archivo = 'ruta_a/mi_archivo.txt'; if(is_writable($archivo)) { echo "El archivo {$archivo} puede ser escrito"; } else { echo "El archivo {$archivo} no puede ser escrito"; } // ahora, verificando si un directorio puede escribirse $directorio = 'ruta/a/mi/carpeta'; if(is_writable($directorio)) { echo "El directorio {$directorio} puede ser escrito"; } else { echo "El directorio {$directorio} no puede ser escrito"; } M�s funciones sobre el sistema de archivos M�s funciones sobre el sistema de archivos, pueden encontrarse en http://www.php.net/manual/es/ref.filesystem.php Procesamiento de texto y manipulaci�n de strings En este cap�tulo, veremos las principales funciones y formas b�sicas, de procesar texto y manipular strings en PHP, las cuales ser�n la base fundamental de las expresiones y algoritmos integrantes de todo sitio Web y aplicaci�n. 110 Programador PHP Experto Eugenia Bahit Ampliando la definici�n de variables de tipo string Como hemos visto anteriormente, una cadena de texto en PHP, puede encerrarse tanto entre comillas simples como entre comillas dobles: $var_1 = 'Esta es una cadena de texto'; $var_2 = "Esta es otra cadena de texto"; Tambi�n disponemos de la posibilidad de delimitar cadenas de texto de gran extensi�n, mediante heredoc (del ingl�s �here document� - documento aqu�), t�pica de una gran parte de lenguajes de programaci�n y shells de Sistemas Operativos basados UNIX, cuya sintaxis es: $variable = <<<IDENTIFICADOR contenido de heredoc IDENTIFICADOR; Donde IDENTIFICADOR, podr� ser cualquier nombre que respete las reglas para nombres de variables y el identificador de cierre, no podr� estar sangrado. Veamos un ejemplo: $documento = <<<NOTA_SOBRE_HEREDOC Es muy importante se�alar que la l�nea con el identificador de cierre no debe contener ning�n caracter, excepto posiblemente un punto y coma (;). Esto significa en particular que el identificador no debe usar sangr�a, y que no deben existir ning�n espacio ni tabulaci�n antes o despu�s del punto y coma. Es muy importante darse cuenta que el primer caracter antes del identificador de cierre debe ser un salto de l�nea definida por el sistema operativo local. En los sistemas UNIX ser�a \n, al igual que en Mac OS X. El delimitador de cierre (posiblemente seguido de un punto y coma) tambi�n debe ser seguido de un salto de l�nea. Si se rompe esta regla y el identificador de cierre no est� "limpio", no ser� considerado como un identificador de cierre, y PHP continuar� buscando uno. Si no se encuentra ning�n identificador de cierre antes del final del fichero, se producir� un error de an�lisis en la �ltima l�nea. NOTA_SOBRE_HEREDOC; 111 Programador PHP Experto Eugenia Bahit Se recomienda el uso de heredoc, para definir cadenas de texto de grandes extensiones. Escapando caracteres Muchas veces es necesario imprimir ciertos caracteres que no pueden simplemente indicarse. Un ejemplo de ello, es cuando en una cadena de texto delimitada por comillas dobles, se desea imprimir el literal de comillas dobles. Hacer esto: $var = "Las comillas dobles (") deben escaparse"; generar� un error, puesto que PHP considerar� el final de la cadena de texto en la segunda comilla doble: $var = "Las comillas dobles (" y encontrar� un error de sintaxis a continuaci�n: ) deben escaparse"; Para solucionar este problema, ciertos caracteres deben escaparse, mediante el uso de una barra diagonal invertida \ $var = "Las comillas dobles (\") deben escaparse"; Caracteres de escape Algunos caracteres de escape pueden representarse como se muestra en la siguiente table: 112 Programador PHP Experto Eugenia Bahit Caracter Significado \n avance de l�nea (LF o 0x0A (10) en ASCII) \r retorno de carro (CR o 0x0D (13) en ASCII) \t tabulador horizontal (HT o 0x09 (9) en ASCII) \v tabulador vertical (VT o 0x0B (11) en ASCII) (desde PHP 5.2.5) \e escape (ESC o 0x1B (27) en ASCII) (desde PHP 5.4.0) \f avance de p�gina (FF o 0x0C (12) en ASCII) (desde PHP 5.2.5) \\ barra invertida \$ signo del d�lar \" comillas dobles \' Comilla simple \{$var} Imprimir las llaves alrededor del contenido de $var $a = "Hola Mundo"; $b = "Yo digo \{$a}"; echo $b; // salida: Yo digo {Hola Mundo} 113 Programador PHP Experto Eugenia Bahit Funciones para manipulaci�n de strings A continuaci�n, se mostrar�n las funciones de uso m�s frecuente para la manipulaci�n de cadenas de texto. Para obtener un listado completo de funciones de string, visita el manual oficial en http://www.php.net/manual/es/ref.strings.php Funciones de escape addslashes($cadena) escapa una cadena de texto a�adiendo barras invertidas a las comillas dobles, siemples, barras invertidas y bytes nulos. escapar cadenas de texto que deban insertarse en bases de datos, y hayan sido recibidas mediante HTTP POST. quotemeta($cadena) escapa una cadena de texto a�adiendo barras invertidas a los siguientes caracteres: . \ + * ? [ ^ ] ( $ ) $doc = "Si se realiza el c�lculo (15*2)+[(12+5)*(4.3+0.45)] obtendremos el importe en $"; $doc = quotemeta($doc); echo $doc; /* Salida: Si se realiza el clculo \(15\*2\)\+\[\(12\+5\)\*\(4\.3\+0\.45\)\] obtendremos el importe en \$ */ De forma inversa a lo anterior, pueden eliminarse las barras invertidas de una cadena espada, mediante stripslashes($cadena) $doc = "Si se realiza el c�lculo (15*2)+[(12+5)*(4.3+0.45)] obtendremos el importe en $"; $doc = quotemeta($doc); 114 Programador PHP Experto Eugenia Bahit echo $doc; /* Salida: Si se realiza el clculo \(15\*2\)\+\[\(12\+5\)\*\(4\.3\+0\.45\)\] obtendremos el importe en \$ */ echo stripslashes($doc); /* Salida: Si se realiza el c�lculo (15*2)+[(12+5)*(4.3+0.45)] obtendremos el importe en $ */ Funciones de conversi�n htmlentities($cadena) convierte los caracteres aplicables a entidades HTML. Esta funci�n debe utilizarse siempre que una cadena de texto deba ser impresa en un documento HTML y se desconozca su contenido, para prevenir que c�digo fuente no deseado, sea ejecutado. $cadena = "Las negritas se escriben entre los tags <b> y </b> mientras que el salto de linea se representa con <br/>"; $cadena = htmlentities($cadena); echo $cadena; /* Las negritas se escriben entre los tags <b> y </b> mientras que el salto de linea se representa con <br/> */ Su opuesto es html_entity_decode($cadena) $cadena = "Las negritas se escriben entre los tags <b> y </b> mientras que el salto de linea se representa con <br/>"; $cadena = html_entity_decode($cadena); echo $cadena; /* Las negritas se escriben entre los tags <b> y </b> mientras que el salto de 115 Programador PHP Experto Eugenia Bahit linea se representa con <br/> */ Cuando solo se deseen convertir a entidades HTML, caracteres especiales tales como & " ' < >, se utilizar� la funci�n htmlspecialchars($cadena) siendo el opuesto de esta �ltima, la funci�n htmlspecialchars_decode($cadena). Evitando ejecuci�n de c�digo no deseado Una funci�n que deber� utilizarse toda vez que quiera evitarse la ejecuci�n de c�digo PHP y HTML no deseado, es strip_tags($cadena, $caracteres_permitidos). Esta funci�n eliminar� todas las etiquetas PHP y HTML exceptuando aquellas que se indiquen como caracteres permitidos: $caracteres_permitidos = "<b>"; $cadena = "<p>Hola <b>Mundo</b></p><script language='javascript'>alert('hola');</script> <?php echo $caracteres_permitidos; ?>"; $resultado = strip_tags($cadena, $caracteres_permitidos); echo $resultado; // salida: Hola <b>Mundo</b>alert('hola'); Eliminar espacios en blanco, tambi�n es posible. Disponemos de tres funciones predefinidas: ltrim($cadena): Elimina los espacios en blanco del inicio de la cadena rtrim($cadena): los elimina del final de la cadena trim(cadena): los elimina del inicio y final de la cadena Funciones de formato La funci�n nl2br($cadena) nos permite convertir saltos de 116 Programador PHP Experto Eugenia Bahit l�nea en su representaci�n HTML (<br />): $cadena = "Esto es una cadena de texto"; $resultado = nl2br($cadena); echo $resultado; /* salida: Esto es<br /> una cadena <br /> de texto */ $cadena = "Esto es\nuna cadena\nde texto"; $resultado = nl2br($cadena); echo $resultado; /* salida: Esto es<br /> una cadena <br /> de texto */ Podemos adem�s, ajustar el ancho de caracteres de una cadena de texto, utilizando la funci�n wordwrap($cadena, $ancho, $salto_de_linea, $no_cortar_palabras). Esta funci�n, recibir� 1 par�metro obligatorio ($cadena) y tres par�metros opcionales: $ancho cantidad de caracteres $salto_de_linea el car�cter o patr�n que se utilizar� para crear el salto de l�nea. Ejemplo: \n o <br/>. $no_cortar_palabras si se establece en TRUE, PHP tendr� cuidado de insertar el salto, sin cortar palabras. $texto = "Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per."; $formato = wordwrap($texto, 60, chr(10), True); echo $formato; 117 Programador PHP Experto Eugenia Bahit /* Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per. */ Podemos necesitar convertir toda una cadena a min�sculas con strtolower($cadena), o solo convertir a min�scula, el primer car�cter de una cadena con lcfirst($cadena): $usuario = "AnGgie"; echo strtolower($usuario); // anggie echo lcfirst($usuario); // anGgie Pero tambi�n podemos querer convertir toda una cadena a may�sculas con strtoupper($cadena), convertir solo el primer car�cter de la cadena ucfirst($cadena) o convertir el primer car�cter de cada palabra ucwords($cadena): $cadena = "hola mundo"; echo strtoupper($cadena); // HOLA MUNDO echo ucfirst($cadena); // Hola mundo echo ucwords($cadena); // Hola Mundo Un ejemplo pr�ctico de conversi�n de may�sculas y min�sculas: $nombre_y_apellido = "Anggie Lopez"; $username = strtolower($nombre_y_apellido); $nombre_visible = ucwords($username); Dar a una cadena, formato de moneda, es posible mediante 118 Programador PHP Experto Eugenia Bahit el uso de money_format($formato, $cadena): setlocale(LC_MONETARY, "es_ES.UTF-8"); $bruto = 178.45; $iva = $bruto * 0.21; $neto = $bruto * 1.21; $bruto_txt = money_format('%(#4n', $bruto); $iva_txt = money_format('%(#4n', $iva); $neto_txt = money_format('%(#4n', $neto); echo $bruto_txt . chr(10); echo $iva_txt . chr(10); echo $neto_txt . chr(10); /* 178,45 � 37,47 � 215,92 � */ AVISO utilizar setlocale antes de dar formato de moneda, asegura la correcta salida de los datos con el s�mbolo monetario correspondiente al idioma y pa�s. Para comprender mejor los posibles patrones de formato que pueden ser utilizados con money_format, acceder a las referencias oficiales en http://www.php.net/manual/es/function.moneyformat. php#refsect1-function.money-format-parameters A veces es preciso formatear un valor num�rico, estableciendo decimales y separadores de decimales y miles. Contamos para ello con la funci�n number_format($numero, $decimales, $separador_decimales, $separador_miles) que retorna el n�mero formateado como cadena de texto: $precio = 12478.493; $precio_txt = number_format($precio, 2, ',', '.'); 119 Programador PHP Experto Eugenia Bahit echo $precio_txt; // 12.478,49 Funciones de manipulaci�n Muchas veces, puede ser muy �til, manipular una cadena de texto, de forma tal, que nos permite operar con diferentes datos. Por ejemplo, es posible dividir una cadena de texto, tomando como punto de divisi�n, un caracter o patr�n, mediante la funci�n explode($delimitador, $cadena) y as� obtener un array con las fracciones de cadena divididas, que nos permita iterar sobre cada una: $contactos = "Juan Antonio Avila <avila@mail.com>, Rodrigo Mancusso <rmancu@mail.com>, Silvina D'laggio <dlaggio@mail.com> "; $patron = "," . chr(10); $personas = explode($patron, $contactos); foreach($personas as $persona) { echo $persona . chr(10); } /* Juan Antonio Avila <avila@mail.com> Rodrigo Mancusso <rmancu@mail.com> Silvina D'laggio <dlaggio@mail.com> */ Podemos contar la cantidad de caracteres de una cadena de texto, mediante la funci�n strlen($cadena): $mensaje = "Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per. Ius id vidit volumus mandamus, vide veritus democritum te nec, ei eos debet libris consulatu. No mei ferri graeco dicunt, ad cum veri accommodare. Sed at malis omnesque delicata, usu et iusto zzril meliore. Dicunt maiorum eloquentiam cum cu, sit summo dolor essent te. Ne quodsi nusquam legendos has, ea dicit voluptua eloquentiam pro, ad sit quas 120 Programador PHP Experto Eugenia Bahit qualisque. Eos vocibus deserunt quaestio ei. "; $caracteres = strlen($mensaje); if($caracteres > 140) { echo "Tu mensaje es demasiado largo. Solo se admiten 140 caracteres."; } Contar la cantidad de palabras en una cadena de texto, e incluso, iterar sobre cada palabra, puede ser algo realmente �til. La funci�n str_word_count($cadena, $formato) nos ayudar� a hacerlo: $nombre_y_apellido = "Juan P."; $datos = str_word_count($nombre_y_apellido, 1); if(count($datos) < 2) { echo "{$nombre_y_apellido} no es un nombre y apellido v�lido"; } else { foreach($datos as $dato) { if(strlen($dato) < 2) { echo "Por favor, no utilices iniciales."; } } } Otra funci�n que podremos utilizar muy a menudo, es str_replace($busqueda, $reemplazo, $cadena) que nos permite buscar un determinado car�cter o patr�n y reemplazarlo por el indicado: $email = "juanperez@dominio.com"; $mail_no_spam = str_replace("@", " [AT] ", $email); echo $mail_no_spam; // juanperez [AT] dominio.com Esta funci�n, admite como par�metros de b�squeda y reemplazo, tanto cadenas de texto, como matrices: $email = "juanperez@dominio.com"; $busqueda = array("@", "."); $reemplazo = array(" [AT] ", " [DOT] "); $mail_no_spam = str_replace($busqueda, $reemplazo, $email); echo $mail_no_spam; // juanperez [AT] dominio [DOT] com 121 Programador PHP Experto Eugenia Bahit Incluso, permite reemplazar todos los elementos de un array de b�zqueda, por un �nico car�cter o patr�n de reemplazo (muy �til para eliminar espacios en blanco en una cadena, como en el siguiente ejemplo): $username = " alejo val3nt1n0 "; $busqueda = array(" ", "\t", "\n", "\r", "\0", "\x0B"); $username = str_replace($busqueda, '', $username); echo $username; // alejoval3nt1n0 Es posible tambi�n, realizar reemplazos, haciendo que la b�squeda sea insensible a may�sculas y min�sculas. Para ello, debemos utilizar la funci�n str_ireplace($busqueda, $reemplazo, $cadena) de la misma forma que lo har�amos con str_replace(). Otra funci�n sumamente �til, es strpos($cadena, $patron_de_busqueda), la cual nos retornar� la posici�n en la que se encuentra el patr�n buscado, dentro de la cedana: $email = "juanperez@mail.com"; $patron = "@"; $posicion = strpos($email, $patron); echo $posicion; // 9 Si se desea que la b�squeda sea insensible a may�sculas y min�sculas, deber� utilizarse stripos($cadena, $patron). Es importante tener en cuenta, que tanto strpos como stripos, retornar�n False cuando el patr�n de b�squeda no sea encontrado. Por la tanto, toda condici�n debe ser comparada por exactitud de valor y tipo de dato: $var1 = "Hola Mundo"; $var2 = "adios mundo"; $patron = "hola"; 122 Programador PHP Experto Eugenia Bahit if(stripos($var1, $patron) === 0) { echo "Est� al comienzo de la cadena" . chr(10); } # INCORRECTO if(stripos($var2, $patron) == 0) { echo "Est� al comienzo de la cadena" . chr(10); } if(stripos($var2, $patron) === False) { echo "No se encontr�" . chr(10); } Manipulando subcadenas en cadenas Hay tres funciones muy �tiles que nos permiten manipular subcadenas de texto dentro de una cadena. La funci�n substr($cadena, $inicio, $longitud) nos retornar� la longitud de la cadena desde el inicio indicado: $cadena = "Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per."; $resumen = substr($cadena, 0, 100); echo "{$resumen}[...]"; /* Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendreri[...] */ Un ejemplo un poco m�s complejo, puede darse con el uso combinado de varias funciones: $patron = "dicit"; $inicio_patron = stripos($cadena, $patron); 123 Programador PHP Experto Eugenia Bahit if($inicio_patron !== False) { echo substr($cadena, $inicio_patron, strlen($cadena)); } /* dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per. */ Con substr_count($cadena, $patron) podremos obtener la cantidad de veces que el patr�n es encontrado en la cadena: $cadena = "Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per."; $patron = "ut"; $apariciones = substr_count($cadena, $patron); echo $apariciones; // 3 Es posible tambi�n, reemplazar una subcadena dentro de una cadena con substr_replace($cadena, $reemplazo, $inicio, $longitud): $cadena = "Ayer recorr� las calles de Liniers con mi primo"; $patron = "las calles de Liniers"; $ini = stripos($cadena, $patron); $nueva_cadena = substr_replace($cadena, "los alrededores de Belgrano", $ini, strlen($patron)); echo $nueva_cadena; // Ayer recorr� los alrededores de Belgrano con mi primo 124 Programador PHP Experto Eugenia Bahit Funciones de encriptaci�n Podemos obtener el hash MD5 con la funci�n md5($cadena): $clave = "pepe_grillo-12_14"; $hash_clave = md5($clave); echo $hash_clave; // 917f2e75f261ba6df7b36a80e1f38241 ADVERTENCIA Nunca utilices conversores MD5 online. Estos conversores, suelen almacenar las cadenas ingresadas asociadas al hash MD5 resultante, lo cual directa o indirectamente, permite realizar una pseudo ingenier�a inversa sobre los hashes MD5, haciendo vulnerables las contrase�as. Siempre que necesites obtener el hash MD5 de alguna cadena, utiliza PHP-CLI: php -r 'echo md5("cadena a hashear");' PHP dispone de otras funciones de cifrado, para varios algoritmos como SHA1 y CRC32. Sin embargo y a pesar de contar con la funci�n de cifrado MD5, disponemos de una funci�n que engloba todo lo anterior, llamada hash($algoritmo_cifrado, $cadena) que nos facilita cifrar una cadena en varios formatos. Los posibles algoritmos de cifrado, pueden obtenerse con la funci�n hash_algos() la cual retorna un array con todos los algoritmos disponibles: php > print_r(hash_algos()); Array ( [0] => md2 [1] => md4 [2] => md5 [3] => sha1 [4] => sha224 [5] => sha256 125 Programador PHP Experto Eugenia Bahit [6] => sha384 [7] => sha512 [8] => ripemd128 [9] => ripemd160 [10] => ripemd256 [11] => ripemd320 [12] => whirlpool [13] => tiger128,3 [14] => tiger160,3 [15] => tiger192,3 [16] => tiger128,4 [17] => tiger160,4 [18] => tiger192,4 [19] => snefru [20] => snefru256 [21] => gost [22] => adler32 [23] => crc32 [24] => crc32b [25] => salsa10 [26] => salsa20 [27] => haval128,3 [28] => haval160,3 [29] => haval192,3 [30] => haval224,3 [31] => haval256,3 [32] => haval128,4 [33] => haval160,4 [34] => haval192,4 [35] => haval224,4 [36] => haval256,4 [37] => haval128,5 [38] => haval160,5 [39] => haval192,5 [40] => haval224,5 [41] => haval256,5 ) Ejemplo (correr este script para ver los resultados): $clave = "tRxc6348-bR129"; $hashes = array(); foreach(hash_algos() as $hash) { $hash_clave = hash($hash, $clave); $hashes[$hash] = $hash_clave; } 126 Programador PHP Experto Eugenia Bahit Resumen de las principales funciones de string Tipo de funci�n Funci�n Descripci�n Escape addslashes Escapa una cadena a�adiendo barras invertidas a � ' \ y bytes nulos quotemeta A�ade barras invertidas delante de . \ + * ? [ ^ ] ( $ ) stripslashes Elimina las barras invertidas de una cadena escapada Conversi�n htmlentities Convierte los caracteres a entidades HTML html_entity_decode Inversa a htmlentities htmlspecialchars Convierte a entidades HTML los siguientes caracteres: & " ' < > htmlspecialchars_dec ode Inversa a htmlspecialchars strip_tags Elimina todos los tags HTML y PHP ltrim Elimina espacios en blanco del comienzo de la cadena rtrim Elimina espacios en blanco del final de la cadena trim Elimina espacios en blanco del comienzo y final de la cadena Formato nl2br Convierte saltos de l�nea en su equivalente HTML wordwrap Ajusta el ancho de caracteres de una cadena strtolower Convierte toda la cadena a min�sculas lcfirst Convierte a min�scula el primer carater de una cadena strtoupper Convierte toda la cadena a may�sculas ucfirst Convierte el primer car�cter de una cadena a may�scula ucwords Convierte el primer car�cter de cada palabra de una cadena a may�sculas money_format Formatea un n�mero con el s�mbolo de moneda correspondiente number_format Formate un n�mero con el separdor de miles y decimales correspondiente 127 Programador PHP Experto Eugenia Bahit Manipulaci�n Explode Divide una cadena generando un array strlen Retorna la longitud de una cadena str_word_count Cuenta la cantidad de palabras str_replace str_ireplace Reemplaza iterativamente un patr�n strpos stripos Retorna la posici�n del patr�n buscado en una cadena substr Retorna una porci�n de la cadena substr_count Retorna la cantidad de apariciones de un patr�n en la cadena substr_replace substr_ireplace Reemplaza iterativamente una porci�n de la cadena Cifrado md5 Retorna el hash MD5 de una cadena hash Retorna el hash de una cadena, cifrado con el algoritmo indicado 128 Programador PHP Experto Eugenia Bahit Taller de Archivos y Procesamiento de Formularios En este taller, crearemos un libro de visitas basado en el sistema de archivos (sin bases de datos) y utilizaremos a la vez, las librer�a de funciones de string, para el procesamiento de los textos. Archivos necesarios para el taller Descarga los archivos que utilizaremos en el taller, desde el siguiente enlace: http://taller-de-php.eugeniabahit.com/taller-de-archivosy- webforms.tar.gz 129 Programador PHP Experto Eugenia Bahit Constantes, variables variables y variables superglobales Durante el taller, hemos introducido cuatro nuevos conceptos, de los cuales, trataremos aqu�, dos de ellos: constantes y variables variables. Para el procesamiento del formulario, tambi�n hemos utilizado la variable superglobal $_POST de PHP y hablaremos de ello. Constantes PHP, a diferencia de otros lenguajes, introduce el concepto de constante. Una constante, para PHP, es un identificador que se utiliza para almacenar datos fijos simples. Se diferencian de las variables, en dos aspectos: � Almacenan datos simples (aunque esto, es un punto discutible) como una cadena de texto, un entero, un flotante, etc. � Una vez definidos no pueden modificarse. Definici�n cl�sica de constantes en PHP Originalmente, PHP requiere del uso de la funci�n define('NOMBRE_DE_LA_CONSTANTE', 'valor') para declarar y definir una constante: define('PRECIO', 25.78); define('PRODUCTO', 'Short de ba�o para ni�o'); 130 Programador PHP Experto Eugenia Bahit define('HAY_STOCK', False); Por convenci�n, el nombre de las constantes se define en letras may�sculas. No obstante, aplican las reglas de nombre para la definici�n de variables. Este tipo de constante, puede definirse en cualquier �mbito de la aplicaci�n, ya sea dentro de una funci�n como fuera de ella. Para llamar a una constante, simplemente se hace referencia a ella, por el nombre: echo PRECIO; // imprime 25.78 Este tipo de constantes, admiten como valor, cualquier tipo de dato simple, incluso, una variable: $nombre = strip_tags($_GET['nombre]); define('NOMBRE', $nombre); Definici�n de constantes en PHP 5.3 Desde la versi�n 5.3 de PHP, se introdujo el uso de la palabra clave const para definir constantes en PHP: const PRECIO = 25.78; const PRODUCTO = 'Short de ba�o para ni�os'; Este tipo de constantes, son las que utilizaremos en nuestros c�digos, puesto que las mismas, introducen un concepto m�s preciso y exacto de lo que es una constante: 131 Programador PHP Experto Eugenia Bahit � Solo pueden declararse en el �mbito global de la aplicaci�n; � Admiten cualquier tipo de dato simple, pero no admiten variables; � El valor de estas constantes, no puede formarse din�micamente (es �constante� en todo sentido); � No pueden ser redeclaradas; Finalidad de las constantes Si bien en los ejemplos anteriores, hemos utilizado un precio y producto para demostrar como definir constantes, la finalidad de �stas, debe ser definir datos no variables inherentes al n�cleo de una aplicaci�n. Para ver un uso pr�ctico y preciso, referirse al taller de archivos y web forms. 132 Programador PHP Experto Eugenia Bahit Variables variables Leer la frase �variables variables� no solo parece redundante, sino adem�s, inexacto y bastante confuso. Lo cierto, es que no existe otra forma de poder llamar a las variables cuyos nombres se forman din�micamente y pueden ser modificados. Es decir, que son �variables� porque aceptan datos que pueden ser modificados y a la vez, vuelven a ser �variables� porque adem�s de sus datos, podemos modificar sus nombre: $nombre_de_variable = 'precio'; $$nombre_de_variable = 25.78; echo $nombre_de_variable; // imprime precio echo $$nombre_de_variable; // imprime 25.78 Esto significa, que el nombre de la variable que almacena el valor 25.78 ser� �precio�. Es decir, que estamos creando una variable, cuyo nombre es din�mico y por tanto, desconocemos, pero podemos acceder a ella, ya que el nombre otorgado, es el valor de otra variable: $a = "mi_variable"; $$a = 75; echo "El nombre de \$\$a es \${$a}"; // salida: El nombre de $$a es $mi_variable 133 Programador PHP Experto Eugenia Bahit Variables superglobales Como hemos podido ver, PHP dispone de variables globales a las cuales se accede mediante el uso de la palabra clave global. As� como existen las variables globales, tambi�n podemos encontrar variables superglobales. Estas variables superglobales, suelen ser arrays asociativos desde los cuales PHP, de forma nativa, nos facilita su acceso desde cualquier parte de la aplicaci�n sin necesidad de utilizar la palabra clave global, ya que son variables internas. A lo largo del curso, hemos utilizado dos variables superglobales: $_GET Un array asociativo tipo clave => valor, de los par�metros pasados al script mediante el m�todo HTTP GET, es decir, par�metros pasados por URL. $_POST Al igual que el anterior, es un array asociativo formado por clave => valor, pero que almacena los datos pasados al script, mediante el m�todo HTTP POST, generalmente, a trav�s de un formulario. Adem�s de $_GET y $_POST, existen otras variables superglobales, que veremos m�s adelante. Una de las m�s importantes, es la variable superglobal $_SERVER que contiene informaci�n del entorno del servidor y de la ejecuci�n. Entre la lista de �ndices (claves) de este array asociativo superglobal, podemos encontrar algunos de uso frecuente como REQUEST_METHOD que nos retorna el m�todo de petici�n HTTP del script en ejecuci�n (POST, GET, PUT o HEAD) o 134 Programador PHP Experto Eugenia Bahit REQUEST_URI que nos devuelve la URI completa que se utiliz� para acceder al script, entre otros. $metodo = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; foreach($_SERVER as $clave=>$valor) { echo "\$_SERVER['$clave'] = $valor<br/>"; } El foreach anterior, generar� una salida similar a la siguiente (se resaltan los keys m�s usuales): $_SERVER['HTTP_HOST'] = localhost $_SERVER['HTTP_CONNECTION'] = keep-alive $_SERVER['HTTP_CACHE_CONTROL'] = max-age=0 $_SERVER['HTTP_USER_AGENT'] = Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11 $_SERVER['HTTP_ACCEPT'] = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 $_SERVER['HTTP_ACCEPT_ENCODING'] = gzip,deflate,sdch $_SERVER['HTTP_ACCEPT_LANGUAGE'] = es-419,es;q=0.8 $_SERVER['HTTP_ACCEPT_CHARSET'] = ISO-8859-1,utf-8;q=0.7,*;q=0.3 $_SERVER['PATH'] = /usr/local/bin:/usr/bin:/bin $_SERVER['SERVER_SIGNATURE'] = $_SERVER['SERVER_SOFTWARE'] = Apache $_SERVER['SERVER_NAME'] = localhost $_SERVER['SERVER_ADDR'] = ::1 $_SERVER['SERVER_PORT'] = 80 $_SERVER['REMOTE_ADDR'] = ::1 $_SERVER['DOCUMENT_ROOT'] = /var/www $_SERVER['SERVER_ADMIN'] = webmaster@localhost $_SERVER['SCRIPT_FILENAME'] = /var/www/euge/file.php $_SERVER['REMOTE_PORT'] = 47578 $_SERVER['GATEWAY_INTERFACE'] = CGI/1.1 $_SERVER['SERVER_PROTOCOL'] = HTTP/1.1 $_SERVER['REQUEST_METHOD'] = GET $_SERVER['QUERY_STRING'] = parametro=valor $_SERVER['REQUEST_URI'] = /euge/file.php?parametro=valor $_SERVER['SCRIPT_NAME'] = /euge/file.php $_SERVER['PHP_SELF'] = /euge/file.php $_SERVER['REQUEST_TIME'] = 1331772401 M�s informaci�n sobre la superglobal $_SERVER puede obtenerse en: http://www.php.net/manual/es/reserved.variables.server.php. 135 Programador PHP Experto Eugenia Bahit Env�o de correo electr�nico con PHP PHP, dispone de una funci�n llamada mail() que permite enviar correos electr�nicos tanto en texto plano como HTML, a trav�s del servidor Web, utilizando la librer�a sendmail (generalmente, instalada por defecto). La funci�n mail() y su sintaxis La funci�n mail() requiere que m�nimamente le sean pasados 3 par�mtros: destinario, asunto y mensaje: $destinatario = "user@mail.com"; $asunto = "Correo electr�nico enviado desde PHP"; $mensaje = "Esta es una prueba de env�o."; mail($destinatario, $asunto, $mensaje); El par�metro �destinatario�: formatos admitidos La funci�n mail() admite como destinatario, una o m�s direcciones de correo electr�nico, debiando mantener alguno de los siguientes formatos: �nico destinatario: user@mail.com Varios destinatarios: user2@mail.com, user2@mail.com, user5@mail.com 136 Programador PHP Experto Eugenia Bahit Destinatario con nombre e e-mail: Juan P�rez <mail@dominio.com> Varios destinatarios con nombre e e-mail: Juan P�rez <mail@dominio.com>, Ana G�mez <mail2@dominio.com> Y l�gicamente, cualquier combinaci�n de las anteriores: user2@mail.com, Ana G�mez <mail2@dominio.com> Cabeceras adicionales como par�metro extra Adicionalmente, pueden sumarse a la funci�n mail() cabeceras adicionales a ser enviadas. Estas cabeceras, pueden utilizarse para agregar destinatarios con cocopia, con copia oculta, direcci�n de respuesta, remitente, tipo de contenido, etc. $destinatario = "user@mail.com"; $asunto = "Correo electr�nico enviado desde PHP"; $mensaje = "Esta es una prueba de env�o."; $cabeceras_adicionales = "From: Ana Mar�a L�pez <anita@mail.com>\r\n"; $cabeceras_adicionales .= "Reply-to: Roc�o Irao <rocio@mail.com>\r\n"; $cabeceras_adicionales . "Cc: Ariel Domingo <ariel@mail.com>\r\n"; $cabeceras_adicionales .= "Bcc: Supervisor <admin@mail.com>\r\n"; mail($destinatario, $asunto, $mensaje, $cabeceras_adicionales); Comprobando que el e-mail pudo enviarse La funci�n mail() retornar� TRUE cuando el env�o del mensaje haya podido concretarse. De lo contrario, retornar� FALSE: if(mail($destinatario, $asunto, $mensaje, $cabeceras)) { echo "El e-mail se ha enviado satisfactoriamente."; 137 Programador PHP Experto Eugenia Bahit } else { echo "Se ha producido un error al intentar enviar el e-mail"; } ADVERTENCIA La funci�n mail() abre un socket SMTP en cada llamada. Si bien puede utilizarse esta funci�n, para realizar env�os iterativos (mediante un bucle for, por ejemplo), se desaconseja iterar sobre esta funci�n en env�os masivos. 138 Programador PHP Experto Eugenia Bahit Enviando mensajes en formato HTML Para poder enviar un mensaje con formato HTML desde PHP, solo ser� necesario, agregar en las cabeceras del correo electr�nico, el Content-type correspondiente: $destinatario = "user@mail.com"; $asunto = "Correo electr�nico enviado desde PHP"; $mensaje = "<p><a href="http://es.wikipedia.org/wiki/Lorem_ipsum">Lorem ipsum</a> ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, <b>quo dicit ridens inciderint id</b>. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per.</p>"; $cabeceras_adicionales = "MIME-Version: 1.0\r\n"; $cabeceras_adicionales .= "Content-type: text/html; charset=utf-8\r\n"; $cabeceras_adicionales .= "From: Ana Mar�a L�pez <anita@mail.com>\r\n"; $cabeceras_adicionales .= "Reply-to: Roc�o Irao <rocio@mail.com>\r\n"; $cabeceras_adicionales .= "Cc: Ariel Domingo <ariel@mail.com>\r\n"; $cabeceras_adicionales .= "Bcc: Supervisor <admin@mail.com>\r\n"; if(mail($destinatario, $asunto, $mensaje, $cabeceras_adicionales)) { echo "El e-mail se ha enviado satisfactoriamente."; } else { echo "Se ha producido un error al intentar enviar el e-mail"; } TIP El mensaje a enviar por correo electr�nico puede ser cualquier tipo de contenido almacenado en una variable. Por lo tanto, puede utilizarse un sistema de plantillas HTML, con file_get_contents(), formatearse y manipularse el contenido e incluso, reemplazar datos din�micamente a trav�s de un formulario. 139 Programador PHP Experto Eugenia Bahit Funciones para el manejo de Fecha y Hora La librer�a de funciones para la manipulaci�n de fechas y horas de PHP, es lo suficientemente amplia, para permitirnos un control absoluto en el manejo de las mismas. Veremos aqu�, aquellas que utilizaremos con mayor frecuencia. Sin embargo, una gu�a completa de referencias de funciones para fecha y hora, puede obtenerse, visitando la documentaci�n oficial en http://nc.php.net/manual/es/ref.datetime.php Funciones simples de fecha y hora Obtener la fecha y hora actual en un array asociativo getdate() es la funci�n indicada para obtener la informaci�n relativa a la fecha y hora actual, en un array asociativo: $datos_fecha_hora = getdate(); print_r($datos_fecha_hora); /* Array ( [seconds] => 3 [minutes] => 53 [hours] => 16 [mday] => 15 [wday] => 4 [mon] => 3 [year] => 2012 [yday] => 74 [weekday] => Thursday [month] => March [0] => 1331841183 ) 140 Programador PHP Experto Eugenia Bahit */ El array asociativo retornado por getdate(), como bien se indica en el manual oficial3, devolver� las siguientes claves: Clave Descripci�n Ejemplo de valores devueltos seconds Representacion num�rica de los segundos 0 a 59 minutes Representacion num�rica de los minutos 0 a 59 hours Representacion num�rica de las horas 0 a 23 mday Representacion num�rica del d�a del mes 1 a 31 wday Representacion num�rica del d�a de la semana 0 (para Domingo) hasta 6 (para S�bado) mon Representacion num�rica de un mes 1 hasta 12 year Una representacion num�rica completa de una a�o, 4 d�gitos Ejemplos: 1999 o 2003 yday Representacion num�rica del d�a del a�o 0 hasta 365 weekday Una representaci�n textual completa del d�a de la semana Sunday hasta Saturday month Una representaci�n textual completa de un mes, como January o March January hasta December 0 Los segundos desde la �poca Unix Dependiente del Sistema, t�picamente -2147483648 hasta 2147483647. Obtener fecha y hora actual con formato en una cadena de texto Una funci�n que hemos utilizado mucho, es date(). Esta funci�n nos permite obtener datos relacionados a la fecha y hora actual, con un formato espec�fico. Este formato, se especifica como par�metro tipo string: echo date('Y-m-d'); // 2012-03-15 Dentro del par�metro tipo string, algunos de los formatos 3 http://nc.php.net/manual/es/function.getdate.php#refsect1- function.getdatereturnvalues 141 Programador PHP Experto Eugenia Bahit combinables de los cuales disponemos, son los siguientes: Car�cter de formato Descripci�n Ejemplo de valores devueltos D�a d D�a del mes, 2 d�gitos con ceros iniciales 01 a 31 D Una representaci�n textual de un d�a, tres letras Mon hasta Sun j D�a del mes sin ceros iniciales 1 a 31 l ('L' min�scula) Una representaci�n textual completa del d�a de la semana Sunday hasta Saturday N Representaci�n num�rica ISO-8601 del d�a de la semana (a�adido en PHP 5.1.0) 1 (para lunes) hasta7 (para domingo) w Representaci�n num�rica del d�a de la semana 0 (para domingo) hasta 6 (para s�bado) z El d�a del a�o (comenzando por 0) 0 hasta 365 Semana W N�mero de la semana del a�o ISO-8601, las semanas comienzan en lunes (a�adido en PHP 4.1.0) Ejemplo: 42 (la 42� semana del a�o) Mes F Una representaci�n textual completa de un mes, como January o March January hasta December m Representaci�n num�rica de una mes, con ceros iniciales 01 hasta 12 M Una representaci�n textual corta de un mes, tres letras Jan hasta Dec n Representaci�n num�rica de un mes, sin ceros iniciales 1 hasta 12 t N�mero de d�as del mes dado 28 hasta 31 A�o L Si es un a�o bisiesto 1 si es bisiesto, 0 si no. Y Una representaci�n num�rica completa de un a�o, 4 d�gitos Ejemplos: 1999 o2003 y Una representaci�n de dos d�gitos de un a�o Ejemplos: 99 o 03 Hora a Ante meridiem y Post meridiem en min�sculas am o pm A Ante meridiem y Post meridiem en may�sculas AM o PM g Formato de 12 horas de una hora sin ceros iniciales 1 hasta 12 G Formato de 24 horas de una hora sin ceros iniciales 0 hasta 23 h Formato de 12 horas de una hora con ceros iniciales 01 hasta 12 142 Programador PHP Experto Eugenia Bahit Car�cter de formato Descripci�n Ejemplo de valores devueltos H Formato de 24 horas de una hora con ceros iniciales 00 hasta 23 i Minutos, con ceros iniciales 00 hasta 59 s Segundos, con ceros iniciales 00 hasta 59 Zona Horaria e Identificador de zona horaria (a�adido en PHP 5.1.0) Ejemplos: UTC, GMT,Atlant ic/Azores I (i may�scula) Si la fecha est� en horario de verano o no 1 si est� en horario de verano, 0 si no. O Diferencia de la hora de Greenwich (GMT) en horas Ejemplo: +0200 P Diferencia con la hora de Greenwich (GMT) con dos puntos entre horas y minutos (a�adido en PHP 5.1.3) Ejemplo: +02:00 T Abreviatura de la zona horaria Ejemplos: EST, MDT... Z �ndice de la zona horaria en segundos. El �ndice para zonas horarias al oeste de UTC siempre es negativo, y para aquellas al este de UTC es siempre positivo. -43200 hasta 50400 Fecha/Hora Completa c Fecha ISO 8601 (a�adido en PHP 5) 2004-02-12T15:19:21+00:00 r Fecha con formato � RFC 2822 Ejemplo: Thu, 21 Dec 2000 16:01:07 +0200 U Segundos desde la �poca Unix (1 de Enero del 1970 00:00:00 GMT) Alternativamente, es posible pasar como par�metro a date() una constante de formato predefinida: echo date(DATE_RSS); // Thu, 15 Mar 2012 18:16:21 -0300 Una lista completa de las constantes de formato predefinidas, puede obtenerse en http://nc.php.net/manual/es/class.datetime.php#datetime.constan ts.types AVISO N�tese que la hora tambi�n puede obtenerse, en formato hora UNIX, con la funci�n time(): 143 Programador PHP Experto Eugenia Bahit http://nc.php.net/manual/es/function.time.php Validar una fecha Podemos validar la veracidad de una fecha, mediante el uso de la funci�n checkdate($mes, $dia, $a�o) teniendo en cuenta que retornar� TRUE cuando se trate de una fecha v�lida, o FALSE o en caso contrario: if(checkdate(12, 25, 2011) === True) { echo "Fecha v�lida"; } else { echo "Fecha no v�lida"; } C�lculo de fecha / hora sencillo Es posible realizar c�lculos sencillos con la fecha y hora, combinando el uso de las funciones date() y time(): $hoy = date('Y-m-d'); $manana = date('Y-m-d', (time() + (1 * 24 * 60 * 60))); $ayer = date('Y-m-d', (time() - (1 * 24 * 60 * 60))); /* (1 * 24 * 60 * 60) equivale a: 1 d�a 24 horas 60 minutos 60 segundos */ echo $ayer . chr(10) . $hoy . chr(10) . $manana . chr(10); /* 2011-11-08 (ayer) 2011-11-09 (hoy) 2011-11-10 (ma�ana) */ Lo anterior, es posible gracias a que la funci�n date() puede recibir opcionalmente como par�metro, una marca de tiempo tipo UNIX (timestamp). Cuando la marca de tiempo no es pasada como par�metro, date() retornar� el formato indicado, 144 Programador PHP Experto Eugenia Bahit teniendo en cuenta la fecha y hora actual. Pero cuando una marca de tiempo le es pasada, formar� esa fecha y hora. Dado que time() retorna la fecha/hora en forma UNIX (timestamp) al sumar o restar mediante otra marca de tiempo, ser� posible obtener el timestamp deseado, que permitir� ser formateado con date(). php > echo 1 * 24 * 60 * 60 ; 86400 php > echo time(); 1331847837 php > $a = 1331847837 + 86400; php > echo $a; 1331934237 php > echo date('Y-m-d', $a); 2012-03-16 Otra forma de obtener la marca de tiempo de una fecha determinada es con la funci�n mktime(): php > echo mktime(); 1331848266 Si mktime() no recibe par�metros, retornar� la marca de tiempo de la fecha y hora actual. De lo contrario, deber� recibir los par�metros correspondientes a hora, minuto, segundo, mes, d�a y a�o. echo mktime(0, 0, 0, 12, 25, 2011); // obtiene la marca de tiempo del 25 de diciembre de 2011 Por lo tanto, podr�amos realizar c�lculos, utilizando mktime() como se muestra a continuaci�n: $dia_hoy = (int)date('d'); $mes_hoy = (int)date('m'); $anio_hoy = (int)date('Y'); $semana_siguiente = mktime(0, 0, 0, $mes_hoy, $dia_hoy+7, $anio_hoy); echo date('Y-m-d', $semana_siguiente); 145 Programador PHP Experto Eugenia Bahit Aunque esta �ltima forma, no es la m�s acertada. Ejemplos pr�cticos de c�lculos basados en fechas �Cu�nto tiempo ha pasado? Problema: El 15 de marzo de 2011, Natalia le coment� a su madre, que comenzar�a a ahorrar dinero para comprar un nuevo ordenador y que a tal fin, todos los d�as guardar�a en una caja de zapatos, $2,75. �Cu�nto dinero habr� ahorrado Natalia a la fecha de hoy? Soluci�n: // obtengo la marca de tiempo para el 15/03/2011 $fecha_inicio = mktime(0, 0, 0, 3, 15, 2011); // obtengo la marca de tiempo para hoy $fecha_fin = mktime(); // obtengo la diferencia timestamp entre ambas fechas $diferencia = ($fecha_fin - $fecha_inicio); // convierto a d�as la diferencia timestamp $dias = $diferencia / (24 * 60 * 60); # d�as que pasaron entre dos fechas // dinero ahorrado x d�a $dinero = 2.75; // obtengo el importe total ahorrado, // multiplicando los d�as x el importe diario $ahorro = $dias * $dinero; // Imprimo el resultado echo $ahorro; 146 Programador PHP Experto Eugenia Bahit �Qu� edad tiene...? Problema: Lucas, naci� el 27 de Septiembre de 1978 �Qu� edad tiene hoy? Soluci�n: // obtengo la marca de tiempo de la fecha de nacimiento $fecha_nacimiento = mktime(0, 0, 0, 9, 27, 1978); // obtengo la marca de tiempo de la fecha actual $hoy = mktime(); // obtengo la diferencia entre fecha de nacimiento y hoy $diferencia = $hoy - $fecha_nacimiento; // obtengo la edad $edad = $diferencia / (365 * 24 * 60 * 60); # a�os que pasaron entre 2 fechas // imprimo la edad echo (int)$edad; �En qu� fecha naci�...? Problema: Luciana tiene hoy, 15 a�os �En qu� fecha pudo haber nacido Luciana? Soluci�n: // edad actual de Luciana en a�os $edad = 15; // probable a�o de nacimiento de luciana $anio = (int)date('Y') - $edad; // probable fecha de nacimiento m�s antigua $probable_mas_antigua = date('d/m/') . ($anio-1); // probable fecha de nacimiento m�s reciente $probable_mas_reciente = date('d/m/') . $anio; echo <<<EOT Luciana tiene que haber nacido despu�s del $probable_mas_antigua y antes 147 Programador PHP Experto Eugenia Bahit o durante el $probable_mas_reciente . EOT; 148 Programador PHP Experto Eugenia Bahit Funciones matem�ticas PHP dispone de una gran galer�a de funciones matem�ticas predefinidas, que pueden encontrarse en la documentaci�n oficial visitando http://www.php.net/manual/es/ref.math.php Muchas de estas funciones, nos resultar�n de gran ayuda, convirti�ndose en funciones de uso frecuente para nuestros programas. Veremos las mismas a continuaci�n. Obtener un n�mero elevado a la potencia pow($base, $potencia) echo pow(2, 3); // 8 echo pow(5, 2); // 25 Obtener el n�mero m�s alto y el n�mero m�s bajo max($valores) - min($valores) $precios = array(12.75, 43.90, 106.60, 9, 35.85); $producto_mas_caro = max($precios); // 106.6 $producto_mas_barato = min($precios); // 9 $mejor_oferta = max(107.75, 109.84); // 109.84 $peor_oferta = min(107.75, 109.84); // 107.75 149 Programador PHP Experto Eugenia Bahit Redondear un n�mero con N cantidad de decimales round($numero, $decimales) $bruto = 1573.94; $alicuota_iva = 10.5; $iva = $bruto * $alicuota_iva / 100; // 165.2637 $iva = round($iva, 2); // 165.26 Redondear un n�mero hacia abajo floor($numero) $bruto = 1573.94; $alicuota_iva = 10.5; $iva = $bruto * $alicuota_iva / 100; // 165.2637 $iva = floor($iva); // 165 Redondear un n�mero hacia arriba ceil($numero) $bruto = 1573.94; $alicuota_iva = 10.5; $iva = $bruto * $alicuota_iva / 100; // 165.2637 $iva = ceil($iva); // 166 150 Programador PHP Experto Eugenia Bahit Obtener un n�mero entero aleatorio rand($numero_minimo, $numero_maximo) $password = rand(199999, 999999); // 158035 Funciones para el manejo de matrices En cap�tulos anteriores, cuando hablamos sobre arrays, pudimos ver varias funciones �tiles para manejar estos tipos m�s complejos. A lo largo de los talleres y ejercicios que hemos hecho, tambi�n pudimos llevar dichas funciones a la pr�ctica. Veremos aqu� una lista de funciones para el manejo de matrices, de forma m�s detallada. No obstante, una lista completa puede obtenerse en http://www.php.net/manual/es/ref.array.php Diviendo y uniendo arrays Dividir un array en matrices m�s peque�as array_chunk($array, $tama�o[, boolean $conservar_claves]) N�tese que el tercer argumento es opcional. Por defecto, array_chunk, crear� nuevas claves en los nuevos array. Pero si se indica TRUE, conserver� estas claves. 151 Programador PHP Experto Eugenia Bahit $personas = array('Juan', 'Emilse', 'Pedro', 'Eliseo', 'Rosa', 'Noelia', 'Raul', 'Esteban', 'Diego'); $grupos = array_chunk($personas, 3); print_r($grupos); /* Array ( [0] => Array ( [0] => Juan [1] => Emilse [2] => Pedro ) [1] => Array ( [0] => Eliseo [1] => Rosa [2] => Noelia ) [2] => Array ( [0] => Raul [1] => Esteban [2] => Diego ) ) */ Con una iteraci�n, incluso, podr�amos asignar los grupos creados a nuevos array: $personas = array('Juan', 'Emilse', 'Pedro', 'Eliseo', 'Rosa', 'Noelia', 'Raul', 'Esteban', 'Diego'); $grupos = array_chunk($personas, 3); foreach($grupos as $numero=>$grupo) { $nombre_array = "grupo_{$numero}"; $$nombre_array = $grupo; } Finalmente, obtendr�amos 3 nuevos arrays, llamados $grupo_0, $grupo_1 y $grupo_2, respectivamente. 152 Programador PHP Experto Eugenia Bahit Obtener la porci�n espec�fica de un array array_slice($array, $desde[, $hasta]) $personas = array('Juan', 'Emilse', 'Pedro', 'Eliseo', 'Rosa', 'Noelia', 'Raul', 'Esteban', 'Diego'); $primeras_3_personas = array_slice($personas, 0, 3); print_r($primeras_3_personas); /* Array ( [0] => Juan [1] => Emilse [2] => Pedro ) */ $personas_restantes = array_slice($personas, 3); print_r($personas_restantes); /* Array ( [0] => Eliseo [1] => Rosa [2] => Noelia [3] => Raul [4] => Esteban [5] => Diego ) */ Combinar dos arrays, utilizando uno para las claves y otro para los valores array_combine($array_claves, $array_valores) $comodines = array('{TITULO}', '{SUBTITULO}'); $valores = array('Manual de PHP', 'Trabajando con arrays'); $datos = array_combine($comodines, $valores); print_r($datos); /* Array ( [{TITULO}] => Manual de PHP [{SUBTITULO}] => Trabajando con arrays ) */ 153 Programador PHP Experto Eugenia Bahit Combinar dos o m�s arrays array_merge($array_1, $array_2[, $mas_arrays]) $grupo_a = array('Eliseo', 'Noemi', 'Santiago'); $grupo_b = array('Diego', 'Cecilia', 'Roman'); $personas = array_merge($grupo_a, $grupo_b); print_r($personas); /* Array ( [0] => Eliseo [1] => Noemi [2] => Santiago [3] => Diego [4] => Cecilia [5] => Roman ) */ Combinar dos o m�s arrays multidimensionales de manera recursiva array_merge_recursive($array_1, $array_2[, $mas_arrays]) $persona_a = array('Nombre'=>'Eliseo', 'Edad'=>25); $persona_b = array('Nombre'=>'Miriam', 'Edad'=>37); $personas = array_merge_recursive($persona_a, $persona_b); print_r($personas); /* Array ( [Nombre] => Array ( [0] => Eliseo [1] => Miriam ) [Edad] => Array ( [0] => 25 [1] => 37 ) 154 Programador PHP Experto Eugenia Bahit ) */ Ordenando Arrays por sus valores Ordenar un array de menor a mayor sort($array) $nombres = array('Noemi', 'Diego', 'Ana', 'Eliseo'); sort($nombres); print_r($nombres); /* Array ( [0] => Ana [1] => Diego [2] => Eliseo [3] => Noemi ) */ Ordenar un array de mayor a menor rsort($array) $nombres = array('Noemi', 'Diego', 'Ana', 'Eliseo'); rsort($nombres); print_r($nombres); /* Array ( [0] => Noemi [1] => Eliseo [2] => Diego [3] => Ana ) */ 155 Programador PHP Experto Eugenia Bahit Ordenar un array de menor a mayor manteniendo la relaci�n con los �ndices asort($array) $nombres = array('Noemi', 'Diego', 'Ana', 'Eliseo'); asort($nombres); print_r($nombres); /* Array ( [2] => Ana [1] => Diego [3] => Eliseo [0] => Noemi ) */ Ordenar un array de mayor a menor manteniendo la relaci�n con los �ndices arsort($array) $nombres = array('Noemi', 'Diego', 'Ana', 'Eliseo'); arsort($nombres); print_r($nombres); /* Array ( [0] => Noemi [3] => Eliseo [1] => Diego [2] => Ana ) */ Ordenando Arrays por su clave Ordenar un array de menor a mayor por su clave ksort($array) 156 Programador PHP Experto Eugenia Bahit $personas = array( 'Nombre' => 'Miguel', 'Apellido' => 'Montero', ); ksort($personas); print_r($personas); /* Array ( [Apellido] => Montero [Nombre] => Miguel ) */ Ordenar un array de mayor a menor por su clave krsort($array) $personas = array( 'Nombre' => 'Miguel', 'Apellido' => 'Montero', 'Talle' => 'XL', ); krsort($personas); print_r($personas); /* Array ( [Talle] => XL [Nombre] => Miguel [Apellido] => Montero ) */ Comparando funciones de ordenamiento de arrays Funci�n Ordena por Mantiene las claves asociadas Orden de clasificaci�n asort valor SI menor a mayor arsort valor SI mayor a menor 157 Programador PHP Experto Eugenia Bahit ksort clave SI menor a mayor krsort clave SI mayor a menor sort valor NO menor a mayor rsort valor NO mayor a menor Agregar y Eliminar elementos de un array Agregar elementos al final del array array_push($array, $valores) $personas = array('Juan', 'Emilio'); array_push($personas, 'Miguel', 'Ana', 'Herminio'); print_r($personas); /* Array ( [0] => Juan [1] => Emilio [2] => Miguel [3] => Ana [4] => Herminio ) */ Agregar elementos al comienzo del array array_unshift($array, $valores) $personas = array('Juan', 'Emilio'); array_unshift($personas, 'Miguel', 'Ana', 'Herminio'); print_r($personas); /* Array ( [0] => Miguel [1] => Ana [2] => Herminio [3] => Juan 158 Programador PHP Experto Eugenia Bahit [4] => Emilio ) */ Eliminar el �ltimo elemento de un array array_pop($array) $personas = array('Juan', 'Emilio', 'Ana'); array_pop($personas); print_r($personas); /* Array ( [0] => Juan [1] => Emilio ) */ Eliminar el primer elemento de un array array_shift($array) $personas = array('Juan', 'Emilio', 'Ana'); array_shift($personas); print_r($personas); /* Array ( [0] => Emilio [1] => Ana ) */ Eliminar valores duplicados en un array array_unique($array) $personas = array('Juan', 'Emilio', 'Ana', 'Emilio'); $personas = array_unique($personas); 159 Programador PHP Experto Eugenia Bahit print_r($personas); /* Array ( [0] => Juan [1] => Emilio [2] => Ana ) */ B�squedas y filtros Contar la cantidad de veces que los elementos aparecen en un array array_count_values($array) $frutas = array('pera', 'manzana', 'pera', 'durazno', 'mel�n', 'sand�a', 'kiwi', 'manzana', 'mel�n', 'pera', 'mandarina', 'naranja', 'lim�n', 'lima', 'pomelo', 'pera'); $repeticiones = array_count_values($frutas); foreach($repeticiones as $fruta=>$veces) { if($veces > 1) { echo "Usted repiti� {$fruta} {$veces} veces" . Chr(10); } } /* Usted repiti� pera 4 veces Usted repiti� manzana 2 veces Usted repiti� mel�n 2 veces */ Contar la cantidad de elementos de un array count($array) $frutas = array('pera', 'manzana', 'durazno'); 160 Programador PHP Experto Eugenia Bahit echo count($frutas); // 3 Obtener la suma matem�tica de los valores de un array array_sum($array) $precios = array(75.40, 93.12, 7, 25.18, 173.60); $total = array_sum($precios); echo $total; // 374.3 Obtener las diferencias entre dos o m�s arrays array_diff($array_1, $array_2[, $array_3, �.]) $frutas_1 = array('pera', 'manzana', 'durazno', 'mel�n', 'sand�a', 'kiwi', 'mandarina', 'naranja', 'lim�n', 'lima', 'pomelo'); $frutas_2 = array('pera', 'manzana', 'durazno', 'mel�n', 'sand�a', 'kiwi', 'mandarina', 'lima', 'pomelo'); $diferencias = array_diff($frutas_1, $frutas_2); echo "Las siguientes frutas no est�n en los 2 arrays:" . Chr(10); foreach($diferencias as $fruta_no_repetida) { echo "- {$fruta_no_repetida}" . Chr(10); } /* Las siguientes frutas no est�n en los 2 arrays: - naranja - lim�n */ 161 Programador PHP Experto Eugenia Bahit Filtrar datos de un array, utilizando una funci�n de retorno array_filter($array, $funcion) $datos = array(25, 43.2, 64.98, 33.7, 'luna', 95, 32, 60.05, 'agua', 'sol'); function retornar_enteros($dato) { if(is_int($dato)) { return $dato; } } function retornar_otros_datos($dato) { if(!is_int($dato)) { return $dato; } } $enteros = array_filter($datos, 'retornar_enteros'); $otros_datos = array_filter($datos, 'retornar_otros_datos'); print_r($enteros); /* Array ( [0] => 25 [5] => 95 [6] => 32 ) */ print_r($otros_datos); /* Array ( [1] => 43.2 [2] => 64.98 [3] => 33.7 [4] => luna [7] => 60.05 [8] => agua [9] => sol ) */ 162 Programador PHP Experto Eugenia Bahit Verificar si un array contiene una clave determinada array_key_exists($clave, $array) if(!array_key_exists('password', $_POST)) { echo 'Debe indicar una contrase�a'; } Obtener todas las claves de un array o todos los valores array_keys($array) - array_values($array) $libro = array( 'Titulo' => 'Manual de PHP', 'Subtitulo' => 'Trabajando con arrays', 'Autor' => 'Eugenia Bahit', 'Fecha' => '12/10/2011', ); $claves = array_keys($libro); $valores = array_values($libro); print_r($claves); /* Array ( [0] => Titulo [1] => Subtitulo [2] => Autor [3] => Fecha ) */ print_r($valores); /* Array ( [0] => Manual de PHP [1] => Trabajando con arrays [2] => Eugenia Bahit [3] => 12/10/2011 ) */ 163 Programador PHP Experto Eugenia Bahit Verificar si un array contiene una valor determinada in_array($valor, $array) if(in_array(50, $puntaje)) { echo 'Usted ha obtenido el m�ximo puntaje posible en una respuesta'; } Buscar un valor detrminado en un array y obtener su clave correspondiente array_search($valor, $array) $personas = array('Juan', 'Ana', 'Emilse', 'Diego'); $persona_buscada = 'Emilse'; $resultado = array_search($persona_buscada, $personas); var_dump($resultado); // int(3) 164 Programador PHP Experto Eugenia Bahit Cookies y Sesiones de usuario �Qu� es una cookie? Una cookie es un archivo de texto plano, que se almacena remotamente -en la m�quina del cliente- a trav�s del navegador. Cada cookie -archivo- es un conjunto de datos que provienen del mismo servidor -m�s precisamente, del mismo dominio-. B�sicamente, cada cookie tendr� asociado a ella, un nombre que la identifique y un valor. Los datos que se almacenan remotamente en el ordenador del cliente, pueden ser de cualquier tipo y el objetivo de estos, es: � Almacenar informaci�n relativa al usuario; � Acceder a esa informaci�n, para realizar seguimientos y acciones personalizadas con respecto a cada usuario en particular De esta forma, podr�amos pedirle a un usuario, que ingrese su nombre mediante un Web Form (por ejemplo, ingresa el nombre �Javier�), almacenar ese dato en una cookie, y as�, cada vez que el usuario ingrese a nuestra aplicaci�n o Sitio Web, buscar�amos esa cookie, acceder�amos a ella, leer�amos los datos y finalmente, podr�amos mostrarle al usuario, un mensaje personalizado, que diga �Hola Javier!�. 165 Programador PHP Experto Eugenia Bahit Vale aclarar entonces, que las cookies se pueden crear, leer, modificar y eliminar, tanto por nuestra aplicaci�n como por el mismo usuario, si �ste es adem�s de curioso, medianamente avezado. Las cookies no son eternas As� como una cookie, posee un nombre y valor asociado, tambi�n puede tener asociada, una fecha de caducidad o per�odo de validez. De esta forma, podemos crear una cookie indicando que expire el 12 de febrero de 2015 y otra, que lo haga dentro de 6 d�as. Pero las cookies, pueden desaparecer antes de lo previsto, ya que al ser archivos pertenecientes al usuario y por tanto, almacenados en su propio ordenador, el usuario podr�a eliminarlos. �Qu� son las sesiones de usuario? Las sesiones de usuario, al igual que las cookies, son una forma de almacenar informaci�n relativa al usuario, que permiten que dicha informaci�n se propague y mantenga activa, con cada acci�n del usuario sobre nuestra App o Sitio Web. Las sesiones, tambi�n son almacenadas remotamente mediante cookies, pero a la vez son retenidas localmente en memoria. A diferencia de las cookies, las sesiones expiran pasado un per�odo de tiempo prestablecido, de inactividad por parte del usuario. Como diferencias fundamentales entre cookies y sesiones, podemos mencionar que: � Las sesiones crean cookies, pero las cookies no crean sesiones; 166 Programador PHP Experto Eugenia Bahit � Las sesiones expiran autom�ticamente por inactividad del usuario, tras un per�odo de tiempo predeterminado, mientras que las cookies expiran en la fecha que se les indique o porque son eliminadas por el usuario; Usos e importancia Como bien hemos dicho antes, tanto cookies como sesiones se utilizan para personalizar la experiencia del usuario. De esta forma, podremos saber que todos los sistemas Web que restringen su acceso mediante contrase�as, pueden hacerlo gracias al uso de cookies y sesiones. Por ello, es tan importante tener dominio tanto de unas como de otras. Lo b�sico Antes de ver como implementar el uso de cookies y sesiones en una aplicaci�n Web, necesitamos conocer c�mo llevar adelante las acciones b�sicas que podemos realizar con las cookies y sesiones. Estas acciones son: crearlas, leerlas, modificarlas y eliminarlas. Creaci�n, lectura, modificaci�n y eliminaci�n de cookies Para realizar acciones con cookies, adem�s de un gran n�mero de funciones, PHP nos brinda un array superglobal denominado $_COOKIE, el cual nos permitir� acceder en todo momento a los datos del usuario. Crear una cookie Para crear una cookie utilizaremos la funci�n setcookie() de PHP. En orden de aparici�n, los par�metros que esta funci�n 167 Programador PHP Experto Eugenia Bahit recibe, son los siguientes: Par�metros obligatorios: 1. Nombre de la cookie. Ejemplo: username 2. Valor. Ejemplo: javier75 Par�metros opcionales: 3. Momento en el que debe expirar. Si no se indica, caduca autom�ticamente. Ejemplo en segundos: time() + 3600 -el equivalente a 1 hora- 4. Directorio en el cu�l es v�lida la cookie. Se debe utilizar '/' para que sea v�lida en todo el dominio. Ejemplo: '/' 5. Dominio. Ejemplo: eugeniabahit.com 6. Solo se transmite por HTTPS. Ejemplo: False 7. Solo se transmite por HTTP. Ejemplo: True. Siempre se recomienza indicar TRUE, a fin de evitar que la cookie pueda ser accedida mediante JavaScript, y por lo tanto, vulnerable a ataques del tipo XSS. $nombre = "nombre y apellido"; $valor = "Eugenia Bahit"; $expira = time() + (3600 * 24 * 365); // 1 a�o $dir = "/"; $dominio = "desa.eugeniabahit.com"; // no ser� v�lida en www.eugeniabahit.com $https = FALSE; $http = TRUE; setcookie($nombre, $valor, $expira, $dir, $dominio, $https, $http); 168 Programador PHP Experto Eugenia Bahit Leer una cookie Para leer una cookie, haremos uso del array superglobal $_COOKIE: echo "Hola {$_COOKIE["nombre y apellido"]}!"; // Hola Eugenia Bahit! Modificar una cookie La forma correcta de modificar una cookie, es sobreescribirla, es decir, volver a crearla: $nombre = "nombre_y_apellido"; $valor = "Juan P�rez"; $expira = time() + (3600 * 24 * 365); $dir = "/"; $dominio = "desa.eugeniabahit.com"; $https = FALSE; $http = TRUE; setcookie($nombre, $valor, $expira, $dir, $dominio, $https, $http); Ahora, la cookie �nombre_y_apellido� tendr� el valor �Juan P�rez�. Eliminar una cookie Para eliminar una cookie, el mejor m�todo es volver a crear la cookie, con valor NULL haciendo que expire antes de la fecha actual: $nombre = "nombre_y_apellido"; $valor = NULL; $expira = time() - (3600 * 24 * 365); // 1 a�o antes $dir = "/"; $dominio = "desa.eugeniabahit.com"; $https = FALSE; $http = TRUE; setcookie($nombre, $valor, $expira, $dir, $dominio, $https, $http); 169 Programador PHP Experto Eugenia Bahit Un ejemplo pr�ctico con Cookies Vamos a crear un script sencillo, que solicite al usuario, el idioma en el que desea leer un art�culo y vamos a guardar su preferencia en una cookie, a fin de que cada vez que visite nuestro sitio Web, podamos decidir en qu� idioma le mostraremos el art�culo. Paso a paso: 1. Crear una carpeta llamada sitio-web-multi-idioma 2. Dentro de ella, vamos a crear otra carpeta llamda paginas 3. Dentro de la carpeta paginas, vamos a crear 2 archivos: hola_en.html hola_es.html 4. En el archivo hola_en.html vamos a escribir cualquier texto en ingl�s, y en el archivo hola_es.html, cualquier texto en espa�ol Ahora, dentro de la carpeta sitio-web-multi-idioma vamos a crear los siguientes archivos: template.html cambiar_idioma.php funciones.php index.php En los cuales, vamos a colocar el c�digo que se describe a continuaci�n. Archivo template.html Ser� nuestra vista HTML, en la cual, mostraremos un formulario que permita al usuario elegir el idioma en el cual ver la p�gina. 170 Programador PHP Experto Eugenia Bahit <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Web Site</title> </head> <body> <header> <h1>Web Site</h1> </header> <nav> <form method="POST" action="cambiar_idioma.php" id="frm_idioma"> <label for="lang">Elija su idioma / choose your language:</label> <select id="lang" name="idioma"> <option value="es">Espa�ol</option> <option value="en">English</option> </select> <input type="submit" value="OK"/> </form> </nav> <article> {PAGINA} <!-- Aqu� se reemplazar� el contenido por el del archivo hola_es.html u hola_en.html seg�n el idioma elegido por el usuario --> </article> </body> </html> Archivo funciones.php En este archivo defineremos todas las funciones necesarias para recibir los datos del formulario, crear la cookie, renderizar el HTML y mostrar la p�gina. <?php # Trae los datos del formulario function get_idioma() { $pagina = "paginas/hola_es.html"; if(isset($_POST['idioma'])) { $idioma = $_POST['idioma']; switch ($idioma) { case 'es': $pagina = "paginas/hola_es.html"; break; case 'en': $pagina = "paginas/hola_en.html"; break; } } 171 Programador PHP Experto Eugenia Bahit return $pagina; } # Modifica el idioma elegido � crea o modifica la cookie function cambiar_idioma() { $pagina = get_idioma(); setcookie("pagina", $pagina, time()+(3600*24*365)); header('Location: index.php'); } # Trae el contenido de la p�gina seg�n el idioma function get_pagina() { if(isset($_COOKIE['pagina'])) { $pagina = $_COOKIE['pagina']; } else { $pagina = "paginas/hola_es.html"; } return file_get_contents($pagina); } # Muestra la p�gina al usuario function mostrar_pagina() { $plantilla = file_get_contents("template.html"); $contenido = get_pagina(); $html = str_replace("{PAGINA}", $contenido, $plantilla); echo $html; } ?> Archivo cambiar_idioma.php A este archivo es enviado el formulario y es quien se encarga de llamar a la funci�n que se ocupa de modificar o crear la cookie con la elecci�n del idioma del usuario. <?php require_once("funciones.php"); cambiar_idioma(); ?> Archivo index.php Este archivo, requerir� tambi�n de funciones.php. Ser� el archivo principal, que se encargue de llamar a la funci�n mostrar_pagina(). 172 Programador PHP Experto Eugenia Bahit <?php require_once("funciones.php"); mostrar_pagina(); ?> Descarga los archivos de este ejemplo desde la Web del curso, en: http://taller-de-php.eugeniabahit.com O mira el ejemplo en marcha ingresando en: http://taller-de-php.eugeniabahit.com/Ejemplos-En- Marcha/sitio-web-multi-idioma/ Trabajando con Sesiones Lleg� la hora de introducirnos m�s a fondo con las sesiones para ir finalizando con la primera parte del curso y, ya adentrarnos en el trabajo con bases de datos. As� que �No perdamos tiempo! Primeros pasos con sesiones Antes de comenzar a crear sesiones -y manipularlas-, es necesario saber, que para poder trabajar con ellas, a diferencia de las cookies, es necesario inicializarlas. Para ello, PHP nos provee de la funci�n session_start(), la cual debe ser llamada siempre, antes de realizar cualquier otra operaci�n relacionada con sesiones: 173 Programador PHP Experto Eugenia Bahit session_start(); AVISO: Al igual que con las cookies, PHP tambi�n nos otorga un array superglobal para acceder a las sesiones, llamado $_SESSION Otra particularidad a tener en cuenta, es que PHP, genera un identificador �nico de sesi�n del usuario, -que nos permitir� utilizarlo para identificar en cada sesi�n y de manera inequ�voca al usuario- al cual se puede acceder, invocando a la funci�n session_id(): session_start(); echo session_id(); Crear una nueva sesi�n Mediante el array superglobal $_SESSION, podemos crear, leer y modificar sesiones, de manera simple y directa: Recuerda que tanto para iniciar una nueva sesi�n como para reanudar una sesi�n existente siempre tendr�s que hacerlo con session_start() sin excepci�n. session_start(); $_SESSION['usuario'] = 'javier75'; // creo la sesi�n 'usuario' 174 Programador PHP Experto Eugenia Bahit Leer una sesi�n Si se desea obtener la ID de sesi�n, habr� que recurrir a la funci�n session_start() como se indic� anteriormente. Para leer una sesi�n creada por nosotros, bastar� con invovar al array superglobal $_SESSION['nombre_de_la_sesion']: session_start(); echo $_SESSION['usuario']; // javier75 Modificar la sesi�n Si se desea modificar la ID de sesi�n, debe pasarse como par�metro a session_id(): session_start(); session_id('nuevoID'); echo session_id(); // nuevoID En cambio, si se desea modificar cualquier variable de sesi�n, creada por nosotros, bastar� con modificar el array superglobal $_SESSION: session_start(); $_SESSION['usuario'] = 'javier_1975'; Eliminar una variable de sesi�n Para eliminar una variable de sesi�n, �sta, puede destruirse mediante unset($_SESSION['nombre_de_la_variable_de_sesion']: 175 Programador PHP Experto Eugenia Bahit session_start(); unset($_SESSION['usuario']); Pero para destruir la sesi�n completa del usuario (incluyendo la ID de sesi�n), se debe recurrir a la funci�n session_destroy() y eliminar la cookie con el nombre de la sesi�n, el cual se obtiene mediante session_name(): session_start(); // reanudo la sesi�n unset($_SESSION); // destruyo todas las variables de sesi�n creadas // obtengo los par�metros de la cookie de sesi�n // los necesitar� para poder destruirla $datos_cookie = session_get_cookie_params(); // sobreescribo la cookie de sesi�n -la eliminosetcookie( session_name(), NULL, time()-999999, $datos_cookie["path"], $datos_cookie["domain"], $datos_cookie["secure"], $datos_cookie["httponly"]); session_destroy(); // destruyo la sesi�n Un caso pr�ctico de uso de sesiones Es el caso de restringir el acceso a ciertas p�ginas de nuestro sitio Web, solo a usuarios con permiso para hacerlo. Crearemos un programa muy simple, solo a modo de ejemplo. Lo primero que haremos -ya que no utilizaremos ning�n sistema de registro y administraci�n de usuarios-, es crear un usuario gen�rico con contrase�a �nica. Para evitar guardar estos datos en texto plano, lo que haremos, es utilizar PHP-CLI, para obtener el hash MD5, de la combinaci�n usuario contrase�a: php > $u = "pepegrillo"; php > $p = "_italia1975_"; php > echo md5($u . $p); 85ce93e9490c0fe6a6431f45c8837de8 176 Programador PHP Experto Eugenia Bahit En el ejemplo que coloqu�, utilizo como usuario gen�rico pepegrillo y como contrase�a _italia1975_ y convierto a ambos (en el mismo paso) a su correspondiente hash MD5 (sin espacios, ni caracteres adicionales de ning�n tipo). De esta forma, cuando el usuario quiera ingresar a nuestras p�ginas restringidas, tendr� que utilizar como nombre de usuario pepegrillo y como clave _italia1975_ Luego, nosotros lo que haremos, ser� hashear los datos que ingrese el usuario y compararlos con nuestro hash original. A continuaci�n, veremos como lograrlo y de que forma mantenerlo logueado en el sistema, a trav�s de sesiones. Una vez creado nuestro hash MD5, crearemos un archivo settings.php destinado a al,acenar variables/constantes de entorno global. <?php # Dejaremos ya iniciada una sesi�n session_start(); # aqu� copiaremos nuestro hash MD5 obtenido con PHP-CLI const HASH_ACCESO = "85ce93e9490c0fe6a6431f45c8837de8"; # formulario.html ser� el que pida el ingreso de user y pass al usuario const PAGINA_LOGIN = "formulario.html"; # esta ser� una p�gina cualquiera, con acceso restringido, a la cual # redirigir al usuario despu�s de iniciar su sesi�n en el sistema const PAGINA_RESTRINGIDA_POR_DEFECTO = "pagina_de_muestra.php"; ?> A continuaci�n, crearemos el formulario HTML, necesario para que el usuario inicie sesi�n en el sistema. Lo llamaremos formulario.html. Este formulario, enviar� los datos por HTTP POST, a otro archivo llamado iniciar.php que crearemos luego. <h1>Ingreso al sistema</h1> 177 Programador PHP Experto Eugenia Bahit <form method="POST" action="iniciar.php"> Usuario: <input type="text" name="user" /><br/> Clave: <input type="password" name="pass" /><br/> <input type="submit" value="Ingresar" /> </form> Ahora, crearemos el archivo principal de nuestro sistema: funciones.php Aqu� almacenaremos todas las funciones necesarias para: � Iniciar la sesi�n � Destruir la sesi�n (desconectar al usuario) � Verificar si el usuario tiene sesi�n iniciada Funciones necesarias Funciones de acceso al sistema /* Traigo los datos recibidos por HTTP POST y retorno el HASH MD5 de ambos */ function get_post_data() { $hash = ""; if(isset($_POST['user']) && isset($_POST['pass'])) { $hash = md5($_POST['user'] . $_POST['pass']); } return $hash; } En la funci�n anterior, primero inicializo un hash vac�o. Luego verifico si user y pass han venido a trav�s de HTTP POST con un valor declarado. De ser as�, hasheo ambos datos (sin riesgos, ya que al cifrarlos con MD5 directamente, no hay posibilidad de que se inyecte c�digo malicioso de ning�n tipo). Finalmente, retorno ese hash (si no vinieron datos, retornar� el hash vac�o). 178 Programador PHP Experto Eugenia Bahit /* Comparo ambos hashes. Si son id�nticos, retorno Verdadero */ function validar_user_y_pass() { $user_hash = get_post_data(); $system_hash = HASH_ACCESO; if($user_hash == $system_hash) { return True; } } /* Esta ser� la funci�n principal, que ser� llamada tras enviar el formulario. Si los datos ingresados coinciden con los esperados, inicio la sesi�n del usuario. Finalmente, redirijo al usuario a la p�gina restringida por defecto (posteriormente crearemos una funci�n que se encargue de ello) */ function login() { $user_valido = validar_user_y_pass(); if($user_valido) { $_SESSION['login_date'] = time(); } goto_page(PAGINA_RESTRINGIDA_POR_DEFECTO); } La funci�n login() genera una variable de sesi�n llamada login_date cuyo valor es la marca de tiempo actual (al momento del logueo). Utilizaremos luego esa variable de sesi�n, para verificar la inactividad del usuario. ADVERTENCIA: N�tese que no se crean otras variables de sesi�n, ni tampoco se almacenan datos privados como usuario o clave, ni sus hashes MD5. Funciones para destruir la sesi�n del usuario Una sola funci�n ser� necesario para cumplir este prop�sito. Esta funci�n, luego ser� invocada por un archivo al que llameremos salir.php (que luego crearemos). 179 Programador PHP Experto Eugenia Bahit Esta funci�n, solo se encargar� de destruir la sesi�n del usuario tal cual se indic� cuando hablamos sobre como desconectar a un usuario del sistema, y finalmente, redirigir� al usuario al formulario de login, haciendo uso de una funci�n que crearemos m�s adelante. # Destruir sesi�n function logout() { unset($_SESSION); $datos_cookie = session_get_cookie_params(); setcookie(session_name(), NULL, time()-999999, $datos_cookie["path"], $datos_cookie["domain"], $datos_cookie["secure"], $datos_cookie["httponly"]); goto_page(PAGINA_LOGIN); } Funciones para verificaci�n y validaci�n de sesiones Primero, me encargar� de obtener los datos del �ltimo acceso del usuario. Para eso, voy a recurrir a la variable de sesi�n llamada login_date: /* Primero verifico que la variable de sesi�n login_date, existe. De ser as�, obtengo su valor y lo retorno. Si no existe, retornar� el entero 0 */ function obtener_ultimo_acceso() { $ultimo_acceso = 0; if(isset($_SESSION['login_date'])) { $ultimo_acceso = $_SESSION['login_date']; } return $ultimo_acceso; } El siguiente paso, ser� verificar el tiempo de inactividad de la sesi�n y actualizarlo: /* Esta funci�n, retornar� el estado de la sesi�n: sesi�n inactiva, retornar� False mientras que sesi�n activa, retornar� True. Al mismo tiempo, se encarga de actualizar la variable de sesi�n login_date, cuando la sesi�n se encuentre activa 180 Programador PHP Experto Eugenia Bahit */ function sesion_activa() { $estado_activo = False; $ultimo_acceso = obtener_ultimo_acceso(); /* Establezco como l�mite m�ximo de inactividad (para mantener la sesi�n activa), media hora (o sea, 1800 segundos). De esta manera, sumando 1800 segundos a login_date, estoy definiendo cual es la marca de tiempo m�s alta, que puedo permitir al usuario para mantenerle su sesi�n activa. */ $limite_ultimo_acceso = $ultimo_acceso + 1800; /* Aqu� realizo la comparaci�n. Si el �ltimo acceso del usuario, m�s media hora de gracia que le otorgo para mantenerle activa la sesi�n, es mayor a la marca de hora actual, significa entonces que su sesi�n puede seguir activa. Entonces, le actualizo la marca de tiempo, renov�ndole la sesi�n */ if($limite_ultimo_acceso > time()) { $estado_activo = True; # actualizo la marca de tiempo renovando la sesi�n $_SESSION['login_date'] = time(); } return $estado_activo; } Finalmente, crearemos una peque�a funci�n, que llame a la anterior, y en caso de recibir como resultado que la sesi�n est� inactiva, desconectar� al usuario del sistema. # Verificar sesi�n function validar_sesion() { if(!sesion_activa()) { logout(); } } Esta funci�n, ser� la que invocaremos desde todas y cada una de las p�ginas, a las cuales querramos restringir su acceso. 181 Programador PHP Experto Eugenia Bahit La funci�n que redirige a los usuarios Como �ltima funci�n, crearemos aquella pendiente, de la cual hemos hablado, que se encargar� de redirigir a los usuarios a otra p�gina de nuestro sistema, utilizando la funci�n header() de PHP. # redirigir al usuario function goto_page($pagina) { header("Location: $pagina"); } Pasos finales Con todo esto, tenemos �el alma� de nuestro sistema de logueo. Ahora solo nos resta crear los archivos pendientes: iniciar.php Llamar� a la funci�n login(). Es quien recibe los datos desde el formulario. <?php require_once("funciones.php"); login(); ?> salir.php Llamar� a la funci�n logout(). Ser� llamado cada vez que el usuario elija desconectarse del sistema (tendremos que proveerle del link correspondiente) <?php require_once("funciones.php"); logout(); ?> 182 Programador PHP Experto Eugenia Bahit pagina_de_muestra.php Es solo a modo de ejemplo. Emula a cualquier p�gina restringida de nuestro sistema, la cual deber� invocar a la funci�n validar_sesion(). Es decir, en esta p�gina (as� como en cualquier otra p�gina restringida), colocaremos todo el contenido de acceso privado, ya sea puramente PHP, como HTML, una mezcla de ambos o mejor a�n, c�digo PHP que invoque y renderize el HTML. Todo, absolutamente todo el contenido de estas p�ginas restringidas, solo ser� visible al usuario si tiene la sesi�n iniciada y activa. De lo contrario, el contenido estar� seguro y no ser� mostrado a usuarios sin sesi�n iniciada o con sesi�n inactiva. <?php require_once("funciones.php"); validar_sesion(); ?> <!-- contenido de ejemplo --> <b>Bienvenido usuario registrado!</b> (<a href="salir.php">Desconectarse</a>) Descarga los archivos de este ejemplo desde la Web del curso, en: http://taller-de-php.eugeniabahit.com O mira el ejemplo en marcha ingresando en: http://taller-de-php.eugeniabahit.com/Ejemplos-En- Marcha/uso-de-sesiones/pagina_de_muestra.php 183 Programador PHP Experto Eugenia Bahit Tratamiento y control de errores En alg�n momento, mientras ejecut�babamos alg�n c�digo PHP, habremos podido notar con bastante frecuencia, que PHP nos arrojaba alg�n tipo de mensaje, cuando nuestro script, conten�a alg�n tipo falla. Estos mensajes de error pueden ser diversos tipos (funciones obsoletas, avisos, advertencias, errores fatales -entre otros-) y su visibilidad, se puede configurar tanto desde el archivo de configuraci�n de PHP (php.ini) como en tiempo de ejecuci�n, mediante la funci�n ini_set(). Sin embargo, antes de decidir qu� tipos de error deben producir mensajes visibles o no, se debe considerar primero, en que entorno estamos trabajando. Como hemos hablado en varias ocasiones, por cuestiones de seguridad, cuanto menos informaci�n sobre el comportamiento interno de nuestra aplicaci�n, le demos al usuario, m�s a salvo estar�. Por lo tanto, como regla general, debemos ocultar todos los errores, cuando la aplicaci�n est� corriendo en un entorno de producci�n. Tipos de errores Como se coment� en p�rrafos anteriores, PHP puede emitir distintos tipos de errores, que van desde el aviso de funciones obsoletas hasta errores fatales. 184 Programador PHP Experto Eugenia Bahit Estos tipos de errores, poseen asociadas constantes predefinidas, que podr�n ser pasadas posteriormente, como segundo par�metro a la funci�n ini_set() a fin de configurar errores en tiempo de ejecuci�n. Entre las constantes predifinidas que m�s nos ocupan, podemos encontrar las siguientes: CONSTANTE DESCRIPCI�N Interrumpe el Script E_ERROR Errores fatales en tiempo de ejecuci�n. SI E_WARNING Advertencias no fatales en tiempo de ejecuci�n NO E_NOTICE Avisos en tiempo de ejecuci�n, que indican que el script encontr� algo que podr�a ser un error u ocurrir en el curso normal de un script NO E_STRICT Sugerencias de cambios al c�digo para ampliar la compatibilidad con versiones posteriores de PHP NO E_DEPRECATED Avisos en tiempo de ejecuci�n, sobre funciones obsoletas NO E_ALL Todos los anteriores (excepto E_STRIC, que reci�n es incluido en E_ALL, desde la versi�n 5.4 de PHP) SI Estos niveles de error, pueden utilizarse de forma combinada, mediante los siguientes operadores: OPERADOR SIGNIFICADO USO | �o� (alternativa) E_NOTICE | E_DEPRECATED (E_NOTICE o E_DEPRECATED) & �y� (concatenaci�n) E_NOTICE & E_DEPRECATED (E_NOTICE y E_DEPRECATED) ~ Negaci�n E_ALL & ~E_NOTICE (E_ALL pero no E_NOTICE) ^ Negaci�n (en tiempo de ejecuci�n) E_ALL ^ E_NOTICE (E_ALL pero no E_NOTICE) 185 Programador PHP Experto Eugenia Bahit Configurando errores en tiempo de ejecuci�n En tiempo de ejecuci�n, mediante la funci�n ini_set() de PHP, se pueden establecer ciertas directivas de configuraci�n, relativas a los errores y registro de los mismos. Entre las directivas m�s comunes, podemos encontrar: DIRECTIVA DESCRIPCI�N / EJEMPLO VALOR POR DEFECTO error_reporting Establece que tipo de errores son reportados ini_set('error_reporting', E_ALL & E_DEPRECATED); E_ALL & ~E_NOTICE display_errors Determina si se deben mostrar o no los errores en pantalla ini_set('display_errors', '0'); String 1 track_errors Indica si el �ltimo error encontrado, estar� disponible a trav�s de la variable $php_errormsg ini_set('track_errors', 'On'); String Off error_prepend_string Cadena a imprimir antes del mensaje de error ini_set('error_prepend_string', 'Error encontrado:'); NULL error_append_string Cadena a imprimir despu�s del mensaje de error ini_set('error_prepend_string', '<hr/>'); NULL Un ejemplo sencillo pero altamente productivo Como comentamos anteriormente, cuando nuestra aplicaci�n corriese en un entorno de producci�n, los errores deber�an ocultarse. Sin embargo, mientras que se est� trabajando en un entorno de desarrollo, podr�an estar visiblemente activos para ayudarnos a depurar nuestro c�digo. 186 Programador PHP Experto Eugenia Bahit Una forma simple de lograr esto, es crear un archivo de configuraci�n para la aplicaci�n (que deba ser importado por todos los archivos de la aplicaci�n), que decida si mostrar o no los errores, seg�n el valor de una constante creada a tal fin, que llameremos PRODUCCION, estableciendo su valor por defecto en False (significar� que estamos en entorno de desarrollo) y al subir a producci�n, la setearemos en True: <?php const PRODUCCION = False; // en entornos de producci�n, cambiar a True if(!PRODUCCION) { ini_set('error_reporting', E_ALL | E_NOTICE | E_STRICT); ini_set('display_errors', '1'); ini_set('track_errors', 'On'); } else { ini_set('display_errors', '0'); } ?> Utilizando el s�mbolo @ para silenciar errores En PHP, es posible silenciar errores anteponiendo una arroba (@) a la instrucci�n que podr�a generar un error. Es una pr�ctica que debe utilizarse con sumo cuidado y siempre que se quiera capturar el error (a pesar de estar silenciado), deber� establecer track_errors en On a fin de obtener dicho error mediante la variable $php_errormsg. Veamos algunos ejemplos: <?php ini_set('error_reporting', E_ALL | E_NOTICE | E_STRICT); ini_set('display_errors', '0'); ini_set('track_errors', 'On'); $archivo = @fopen('archivo_que_no_existe.txt', 'r'); if(!$archivo) { 187 Programador PHP Experto Eugenia Bahit echo $php_errormsg; } ?> En el ejemplo anterior, silenciamos el posible error al intentar abrir un archivo mediante fopen(), pero imprimimos en pantalla el mensaje de error capturado, mediante la variable $php_errormsg obteniendo como resultado: fopen(archivo_que_no_existe.txt): failed to open stream: No such file or directory Sin embargo, podr�amos ocultar esta informaci�n al usuario: <?php ini_set('error_reporting', E_ALL | E_NOTICE | E_STRICT); ini_set('display_errors', '0'); ini_set('track_errors', 'On'); $archivo = @fopen('archivo_que_no_existe.txt', 'r'); if(!$archivo) { echo 'Ha ocurrido un error en el sistema. Disculpe las molestias.'; } ?> De esta forma, el usuario solo ver� el siguiente mensaje: Ha ocurrido un error en el sistema. Disculpe las molestias. 188 Programador PHP Experto Eugenia Bahit Trabajando con Bases de Datos MySQL Con este cap�tulo, llegamos al final del curso �PHP para Principiantes�. Abarcando esta �ltima unidad, ya estaremos en condiciones de crear aplicaciones funcionales de alto nivel, de complejidad mendia. Sin dudas, el trabajo con bases de datos, es lo m�s esperado por cualquier programador que est� dando sus primeros pasos, pero entonces �por qu� dejarlo para el final? Y la respuesta a esta pregunta, es muy simple: porque las bases de datos son el �cristal� de una aplicaci�n. Representan la parte m�s vulnerable de un sistema inform�tico y de su vulnerabilidad, depender� la estabilidad o inestabilidad de todo el sistema. A lo largo del curso, hemos adquirido todas las t�cnicas, pr�cticas y herramientas necesarias, para saber como filtrar y securizar datos y reci�n ahora, estamos listos para poder comenzar a trabajar con bases de datos, en absoluta libertad y confianza. �Comencemos! Una base de datos representa un conjunto de datos pertenecientes a un mismo contexto, que son almacenados de forma sistem�tica para su posterior uso. Para comprender mejor el concepto de Base de Datos, por favor, dirigirse a http://es.wikipedia.org/wiki/Base_de_datos 189 Programador PHP Experto Eugenia Bahit Acerca de MySQL MySQL es un servidor de Bases de Datos SQL (Structured Query Language) que se distribuye en dos versiones: � Una versi�n GPL (Software Libre) � Otra versi�n privativa, llamada MySQL AB En este curso, utilizaremos la versi�n estandar licenciada bajo la GNU General Public License (GPL). Instalaci�n y configuraci�n de MySQL Para instalar MySQL, por l�nea de comandos, escribe: sudo apt-get install mysql-server mysql-client Durante la instalaci�n, el sistema te pedir� que ingreses una contrase�a para la administraci�n de MySQL. Asigna una contrase�a que puedas recordar f�cilmente y mantenla a salvo ya que deber�s utilizarla frecuentemente. Una vez que finalice la instalaci�n, ejecuta el siguiente comando a fin de securizar el servidor MySQL (esta configuraci�n, es v�lida tambi�n, para servidores de producci�n): sudo mysql_secure_installation A continuaci�n, el sistema te pedir� que ingreses la contrase�a actual para administraci�n de MySQL (la del usuario root de MySQL). Ten en cuenta que la contrase�a no ser� mostrada mientras escribes: Enter current password for root (enter for none): 190 Programador PHP Experto Eugenia Bahit A continuaci�n, te preguntar� si deseas modificar esa contrase�a. Salvo que desees modificarla, ingresa n: Change the root password? [Y/n] n Ahora la pregunta, ser� si deseas eliminar usuarios an�nimos. Responde que s�: Remove anonymous users? [Y/n] Y Luego, te preguntar� si desees desabilitar el acceso remoto al usuario root de MySQL. Por supuesto, responde que s�: Disallow root login remotely? [Y/n] Y La siguiente pregunta ser� si deseas eliminar la base de datos de prueba y el acceso a ella. Tambi�n responde que s�: Remove test database and access to it? [Y/n] Y Finalmente, te preguntar� si deseas recargar las tablas de privilegios (esto es para asegurar que todos los cambios realizados surjan efecto). Entonces, responde s�, por �ltima vez: Reload privilege tables now? [Y/n] Y Iniciar, reiniciar y detener el servidor MySQL En ocasiones necesitar�s iniciar, reiniciar o detener el servidor de bases de datos, MySQL. Las opciones disponibles son: 191 Programador PHP Experto Eugenia Bahit stop detiene el servidor start inicia el servidor restart reinicia el servidor Para iniciar, reiniciar o detener el servidor, deber�s ejecutar el siguiente comando, seguido de la opci�n deseada: sudo /etc/init.d/mysql opcion_deseada L�gicamente reemplazando opcion por stop, start o restart seg�n si deseas parar, iniciar o reiniciar el servidor. 192 Programador PHP Experto Eugenia Bahit Administraci�n de MySQL Una vez que comencemos a utilizar bases de datos, necesitar�s poder acceder a las opciones de aministraci�n de las mismas. Por lo tanto, te recomiendo tener siempre a mano este cap�tulo, para poder consultarlo con frecuencia. Conectarse y desconectarse al servidor Para conectarte deber�s ejecutar el siguiente comando: mysql -u root -p A continuaci�n, deber�s ingresar la contrase�a del root de MySQL (no es la del root del SO. Es la que hemos configurado durante la instalaci�n de MySQL). Las -u y -p significan usuario y password respectivamente. Te aparecer� un shell interactivo para MySQL: mysql> All� podremos escribir los comandos necesarios para administrar el servidor de bases de datos. Comandos para administrar MySQL dede el shell interactivo La siguiente tabla describe los comandos de uso frecuente que necesitar�s para administrar el servidor de bases de datos desde el shell interactivo. Es una buena idea, imprimir esta tabla para tenerla siempre a 193 Programador PHP Experto Eugenia Bahit mano :) COMANDO DESCRIPCI�N show databases; Muestra todas las bases de datos creadas en el servidor use nombre_de_la_base_de_datos; Indicar que vas a comenzar a utilizar la base de datos elegida create database nombre_de_la_db; Crear una nueva base de datos quit Salir del shell interactivo 194 Programador PHP Experto Eugenia Bahit Sobre el lenguaje SQL SQL -siglas de Structured Query Language-, es el lenguaje de consultas a bases de datos, que nos permitir� crear, modificar, consultar y eliminar tanto bases de datos como sus tablas y registros, desde el shell interactivo de MySQL y tambi�n desde PHP. Como todo lenguaje inform�tico, posee su propia sintaxis, tipos de datos y elementos. En este curso, abordaremos los conceptos b�sicos sobre SQL que nos permitan desarrollar aplicaciones de media complejidad, sin profundizar en el lenguaje en s�, sino solo en aquellos aspectos m�nimamente necesarios relacionados con MySQL. Tipos de datos m�s comunes (recomendados) La siguiente tabla, muestra los tipos de datos m�s comunes, aceptados por versiones la versi�n 5.0.3 o superior, de MySQL. Tipo de dato Denominaci�n Especificaciones Ejemplo Entero INT(N) N = cantidad de d�gitos INT(5) N�mero decimal DECIMAL(N, D) N = cantidad de d�gitos totales D = cantidad de decimales DECIMAL(10, 2) Booleano BOOL BOOL Fecha DATE DATE Fecha y hora DATETIME DATETIME Fecha y hora autom�tica TIMESTAMP TIMESTAMP Hora TIME TIME A�o YEAR(D) D = cantidad de d�gitos YEAR(4) 195 Programador PHP Experto Eugenia Bahit (2 o 4) Cadena de longitud fija CHAR(N) N = longitud de la cadena � entre 0 y 255 CHAR(2) Cadena de longitud variable VARCHAR(N) N = longitud m�xima de la cadena � entre 0 y 65532 VARCHAR(100) Bloque de texto de gran longitud variable BLOB BLOB Sint�xis b�sica de las sentencias SQL Una sentencia SQL (denominada �query� en la jerga inform�tica), es una instrucci�n escrita en lenguaje SQL. Veremos aqu�, el tipo de sentencias m�s habituales. Crear tablas en una base de datos Sintaxis: CREATE TABLE nombre_de_la_tabla( nombre_del_campo TIPO_DE_DATO, nombre_de_otro_campo TIPO_DE_DATO ); Ejemplo: CREATE TABLE productos( producto VARCHAR(125), descripcion BLOB, precio DECIMAL(6, 2), en_stock BOOL ); Explicaci�n: CREATE TABLE productos Crear una nueva tabla llamada �productos� 196 Programador PHP Experto Eugenia Bahit producto VARCHAR(125), Crear un campo llamado producto, de tipo cadena de texto de longitud variable, con una longitud m�xima de 125 caracteres descripcion BLOB, Crear un campo llamado descripci�n, de tipo bloque de texto de gran longitud precio DECIMAL(6, 2), Crear un campo precio de tipo num�rico de longitud m�xima de 6 dig�tos de los cuales, solo 2 pueden ser decimales en_stock BOOL Crear un campo llamado �en_stock� del tipo booleano 197 Programador PHP Experto Eugenia Bahit Insertar datos en una tabla Sintaxis: INSERT INTO nombre_de_la_tabla(campo1, campo2, campo10..) VALUES(dato1, dato2, dato10...); Ejemplo: INSERT INTO productos(producto, precio, en_stock) VALUES('Bolsa de dormir para alta monta�a', 234.65, TRUE); Explicaci�n: INSERT INTO productos(producto, precio, en_stock) Insertar un nuevo registro en los campos producto, precio y en_stock de la tabla productos VALUES('Bolsa de dormir para alta monta�a', 234.65, TRUE); Con los valores �Bolsa de dormir para alta monta�a�, 234.65 y verdadero, respectivamente en cada uno de los campos indicados Seleccionar registros Sintaxis: SELECT campo1, campo2, campo10 FROM tabla; Ejemplo: SELECT producto, precio 198 Programador PHP Experto Eugenia Bahit FROM productos; Explicaci�n: SELECT producto, precio Seleccionar los campos producto y precio FROM productos; De la tabla productos Modificar registros Sintaxis: UPDATE tabla SET campo1 = valor, campo2 = valor, campo10 = valor; Ejemplo: UPDATE productos SET en_stock = FALSE, precio = 0; Explicaci�n: UPDATE productos Actualizar la tabla productos SET en_stock = FALSE, Modificar el campo en_stock por falso precio = 0; y el campo precio a 0 199 Programador PHP Experto Eugenia Bahit Eliminar registros Sintaxis: DELETE FROM tabla; Ejemplo: DELETE FROM productos; Explicaci�n: DELETE FROM productos; Eliminar todos los registros de la tabla productos 200 Programador PHP Experto Eugenia Bahit Consultas avanzadas Si bien no veremos aqu� consultas realmente complejas, ya que el curso se basa en el lenguaje de programaci�n PHP y no, en el lenguaje de consulta SQL, haremos un r�pido paseo, por las opciones disponibles en SQL para sentencias m�s complejas que las anteriores. La cl�usula WHERE Las sentencias en SQL, se componen de cl�usulas. Y WHERE es una de ellas. La cl�usula WHERE nos permite filtrar registros en una sentencia SQL. Esta cl�usula, funciona de forma similar a la comparaci�n de expresiones en PHP, utilizando los siguientes operadores de comparaci�n: > mayor que < menor que = igual que <> distinto que >= mayor o igual que <= menor o igual que BETWEEN n1 AND n2 entre n1 y n2 IS NULL|TRUE|FALSE es nulo | es verdadero | es falso IN(valor1, valor2, va...) contiene Por supuesto, tambien admite operadores l�gicos: AND (y) NOT (negaci�n) OR (o) 201 Programador PHP Experto Eugenia Bahit Veamos algunos ejemplos: Seleccionar productos donde precio sea menor que 1000: SELECT producto, precio FROM productos WHERE precio < 1000; Aumentar el 10% del precio de los productos, que actualmente se encuentren entre 150 y 200: UPDATE productos SET precio = (precio * 1.10) WHERE precio BETWEEN 150 AND 200; Seleccionar productos donde en_stock no sea falso SELECT producto, precio FROM productos WHERE en_stock IS NOT FALSE; Eliminar productos cuyos precios sean 100, 200 y/o 300 y adem�s, en_stock sea falso o producto sea nulo: DELETE FROM productos WHERE precio IN(100, 200, 300) AND (en_stock IS FALSE OR producto IS NULL); Modificar en_stock a verdadero donde precio sea menor que 50 y producto no sea nulo: UPDATE productos SET en_stock = TRUE WHERE precio < 50 AND en_stock IS NOT NULL; 202 Programador PHP Experto Eugenia Bahit Ordenando consultas: la cl�usula ORDER BY Es posible adem�s, ordenar los resultados de una consulta, en forma ascendente (ASC) o descendente (DESC): SELECT producto, descripcion, precio FROM productos WHERE precio BETWEEN 1 AND 50 AND en_stock IS NOT FALSE ORDER BY precio DESC; Tambi�n es posible, ordenar los resultados de la consulta, por m�s de un campo: SELECT producto, descripcion, precio FROM productos WHERE precio BETWEEN 1 AND 50 AND en_stock IS NOT FALSE ORDER BY precio DESC, producto ASC; Alias de tablas y campos Otra posibilidad que nos da el lenguaje SQL, es utilizar alias para el nombre de los campos y las tablas. Estos alias se asignan mediante la palabra clave reservada, AS: SELECT producto AS 'Nombre del Producto', descripcion AS Detalles, precio AS Importe FROM productos AS p WHERE precio BETWEEN 1 AND 50 AND en_stock IS NOT FALSE ORDER BY precio DESC, 203 Programador PHP Experto Eugenia Bahit producto ASC; N�tese que los alias que contengan caracteres extra�os, deben ser encerrados entre comillas simples Funciones del lenguaje SQL de MySQL Es posible tambi�n, utilizar diversas funciones propias del lenguaje SQL -ya sea estandar o de MySQL- a fin de poder obtener los datos con cierto formato. Veremos aquellas de uso m�s frecuente. Contar la cantidad de registros: COUNT() SELECT COUNT(producto) AS Cantidad FROM productos; Sumar totales: SUM() SELECT SUM(precio) AS Total FROM productos; Concatenar cadenas: CONCAT() SELECT producto, CONCAT('USD ', precio, '.-') AS Precio FROM productos; N�tese que las cadenas de caracteres deben encerrarse entre comillas simples y que el operador de concatenaci�n para esta funci�n, es la coma. 204 Programador PHP Experto Eugenia Bahit Convertir a min�sculas y may�sculas: LCASE() y UCASE() SELECT UCASE(producto), LCASE(descripcion) FROM productos; Reemplazar datos: REPLACE() SELECT REPLACE(descripcion, '\n', '<br/>') AS Descripcion FROM productos; Reemplaza '\n' por '<br/>' Obtener los primeros o �ltimos caracteres: LEFT() y RIGHT() SELECT LEFT(producto, 50) FROM productos; Redonder n�meros: ROUND() SELECT ROUND(precio, 2) FROM productos; Retornar� los precios con 2 decimales Obtener solo la fecha de un campo DATETIME o TIMESTAMP: DATE() SELECT DATE(campo_datetime) FROM tabla; Obtener una fecha formateada: DATE_FORMAT() SELECT DATE_FORMAT(campo_fecha, '%d/%m/%Y') FROM tabla; Aplican los mismos patrones de formato de fecha que en PHP 205 Programador PHP Experto Eugenia Bahit Obtener el registro con el valor m�ximo y m�nimo: MAX() y MIN() SELECT MAX(precio) FROM productos; Retorna el producto con el precio m�s caro SELECT MIN(precio) FROM productos; Retorna el producto con el precio m�s barato Optimizaci�n de bases de Datos A continuaci�n, encontrar�s una lista de consejos que SIEMPRE debes seguir, al momento de crear nuevas tablas y escribir sentencias SQL. Todos los registros deben tener un ID �nico Cuando crees tablas, as�gnales un campo id de tipo autonum�rico incremental y establ�celo como �ndice primario. Cuando agregues registros, este campo se completar� autom�ticamente, con un n�mero incremental, que te servir� para optimizar tus consultas y contar con un campo que te permita reconocer el registro como �nico. CREATE TABLE productos( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, producto VARCHAR(125) ); El campo id, ser� como cualquier otro y lo podr�s seleccionar en un SELECT o utilizarlo e cualquier cl�usula WHERE. 206 Programador PHP Experto Eugenia Bahit Crear �ndices en las tablas Todas las tablas deben tener un �ndice. El �ndice se asigna a uno o m�s campos, y es utilizado por SQL para filtrar registros de forma m�s r�pida. Debes crear �ndices con precauci�n, ya que de la misma forma que se aceleran las consultas, se retrasa la inserci�n y actualizaci�n de registros, puesto que la base de datos, deber� actualizar los �ndices cada vez que se agreguen o modifiquen datos. Cuando una consulta es ejecutada, MySQL tratar� de encontrar primero la respuesta en los campos �ndice, y lo har� en el orden que los �ndices hayan sido creados. �Cu�ndo agregar �ndices? Cuando vayas a utilizar una combinaci�n de campos en la cl�usula WHERE. Por ejemplo, si filtrar�s a menudo, los datos de la tabla producto por su campo precio y en_stock, que precio y en_stock sean un �ndice de m�ltiples campos: CREATE TABLE productos( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, producto VARCHAR(125), precio DECIMAL(10, 2), en_stock BOOL, descripcion BLOB, INDEX(precio, en_stock) ); Indica cu�les campos no pueden ser nulos SQL te da la posibilidad de indicar qu� campos no pueden estar nulos. Indicar que un campo no debe estar nulo, te ayudar� a no almacenar registros defectuosos en tu base de datos. CREATE TABLE productos( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, producto VARCHAR(125) NOT NULL, 207 Programador PHP Experto Eugenia Bahit precio DECIMAL(10, 2) NOT NULL, en_stock BOOL, descripcion BLOB NOT NULL, INDEX(precio, en_stock) ); Utiliza el motor InnoDB El motor de bases de datos InnoDB, te permitir� crear tablas relaciones optimizando su rendimiento. Al momento de crear tus tablas, indica que utilizar�s el motor InnoDB: CREATE TABLE productos( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, producto VARCHAR(125) NOT NULL, precio DECIMAL(10, 2) NOT NULL, en_stock BOOL, descripcion BLOB NOT NULL, INDEX(precio, en_stock) ) ENGINE=InnoDB; Obtener mayor informaci�n � Descarga el Manual de MySQL � Aprende sobre el lenguaje SQL , gratis en 1KeyData 208 Programador PHP Experto Eugenia Bahit Trabajando con MySQL desde PHP Antes de comenzar, deseo aclarar que -por cuestiones de seguridad- en nuestros archivos PHP, solo nos conectaremos a una base de datos, para realizar consultas de selecci�n, modificaci�n, inserci�n y eliminaci�n de registros en tablas y bases de datos existentes. No crearemos tablas ni bases de datos desde nuestros archivos PHP, sino que lo haremos desde el administrador de MySQL por l�nea de comandos. A fin de poder trabajar con los ejemplos de este cap�tulo, comenzaremos creando una nueva base de datos, con la tabla que necesitaremos. Ejecuta las siguientes sentencias, desde el Shell interactivo de MySQL: CREATE DATABASE curso_php; USE curso_php; CREATE TABLE usuarios( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(24) NOT NULL, email VARCHAR(100) NOT NULL, password VARCHAR(40) NOT NULL, suspendido BOOL, fecha_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX(username, password, suspendido) ) ENGINE=InnoDB; AVISO: PHP cuenta con dos tipos de conectores MySQL: mysql y mysqli, disponible desde la versi�n 5. Si bien mysqli y m�s potente, seguro y estable que mysql, en este cap�tulo, veremos como utilizar ambos. 209 Programador PHP Experto Eugenia Bahit Antes de continuar, vamos a instalar el m�dulo de MySQL para PHP: sudo apt-get install php5-mysql MySQL desde PHP con el conector mysql Veremos como utilizar el conector mysql, a solo efecto educativo, pero para nuestras aplicaciones, utilizaremos mysqli. Para trabajar con bases de datos desde PHP con el conector mysql, el procedimiento, consiste en los siguientes pasos: 1. Conectarse a la base de datos 2. Seleccionar la base de datos a utilizar 3. Ejecutar una consulta 4. Capturar los resultados 5. Liberar los resultados 6. Cerrar la conexi�n Conectarse a la base de datos $host = 'localhost'; $usuario = 'root'; $clave = 'contrase�a'; $conn = mysql_connect($host, $usuario, $clave) or die('No me pude conectar a la base de datos'); 210 Programador PHP Experto Eugenia Bahit Seleccionar una base de datos $db = 'curso_php'; mysql_select_db($db) or die('No pude seleccionar la base de datos'); Ejecutar una consulta simple $sql = " INSERT INTO usuarios (username, email, password) VALUES ('juanperez', 'jperez@algundominio.ext', '26ec07ef61f135494b79a13674a9a4ae') "; $result = mysql_query($sql) or die('No pude ejecutar la consulta'); Ejecutar una consulta de selecci�n m�ltiple y capturar sus resultados $sql = " SELECT id, username, email FROM usuarios "; $result = mysql_query($sql) or die('No pude ejecutar la consulta'); Capturamos el array con los resultados while($registros[] = mysql_fetch_array($result)); El array devuelto ($registros) ser� del tipo multidimensional, con un formato como el siguiente: $registros = array( array('campo1'=>'valor', 'campo2'=>'valor', 'campo7'=>'valor'), array('campo1'=>'valor', 'campo2'=>'valor', 'campo7'=>'valor'), ); 211 Programador PHP Experto Eugenia Bahit Es decir, que ser� un array, conteniendo otro array por cada registro encontrado. Liberar los resultados mysql_free_result($result); Cerrar la conexi�n mysql_close($conn); 212 Programador PHP Experto Eugenia Bahit Algunos ejemplos concretos Consulta de selecci�n # Preparo los datos para conectarme a la DB $host = 'localhost'; $usuario = 'root'; $clave = 'contrase�a'; $db = 'curso_php'; # me conecto a la DB $conn = mysql_connect($host, $usuario, $clave) or die('No me pude conectar a la base de datos'); # Selecciono la DB a utilizar mysql_select_db($db) or die('No pude seleccionar la base de datos'); # Preparo la sentencia SQL $sql = " SELECT id, username, email FROM usuarios WHERE suspendido IS NOT TRUE "; # Ejecuto la consulta $result = mysql_query($sql) or die('No pude ejecutar la consulta'); # Capturo los resultados while($registros[] = mysql_fetch_array($result)); # Libero los resultados mysql_free_result($result); # Cierro la conexi�n mysql_close($conn); # Imprimo los resultados print_r($registros); Insertar varios registros en un solo paso # Preparo los datos para conectarme a la DB $host = 'localhost'; $usuario = 'root'; $clave = 'contrase�a'; $db = 'curso_php'; # me conecto a la DB $conn = mysql_connect($host, $usuario, $clave) 213 Programador PHP Experto Eugenia Bahit or die('No me pude conectar a la base de datos'); # Selecciono la DB a utilizar mysql_select_db($db) or die('No pude seleccionar la base de datos'); # Preparo la sentencia SQL $sql = " INSERT INTO usuarios (username, email, password) VALUES ('javier75', 'javi75@algundominio.ext', '26ec07ef61f135494b79a13674a9a4ae'), ('noelia', 'noe@algundominio.ext', '26ec07ef61f135494b79a13674a9a4ae'), ('ana_AR', 'anita@algundominio.ext', '26ec07ef61f135494b79a13674a9a4ae') "; # Ejecuto la consulta mysql_query($sql) or die('No pude ejecutar la consulta'); # Cierro la conexi�n mysql_close($conn); 214 Programador PHP Experto Eugenia Bahit MySQL desde PHP con el conector mysqli Como comentamos anteriormente, mysqli es un conector mucho m�s seguro y potente que mysql. Si bien la forma m�s acertada de implementar este conector, es a trav�s de objetos (es decir, utilizando el paradigma de la programaci�n orientada a objetos), nos concentraremos solo en estilo por procedimientos, ya que �sta, es la t�cnica de programaci�n utilizada en este curso inicial. VENTAJA DE UTILIZAR EL CONECTOR MYSQLI En el caso de mysqli, contamos con una gran ventaja al momento de preparar nuestras sentencias SQL: podemos utilizar �comodines�, para indicar al conector, qu� datos deben ser reemplazados din�micamente. De esta forma, el propio conector se encargar� de filtrar los datos din�micos, obteniendo consultas mucho m�s seguras. Por consiguiente, para trabajar con el conector mysqli, seguiremos estos pasos: 1. Conectarse a la base de datos y seleccionar la DB a utilizar 2. Preparar la consulta 3. Ejecutar una consulta 4. Capturar los resultados 5. Cerrar consulta 6. Cerrar la conexi�n 215 Programador PHP Experto Eugenia Bahit Abrir una conexi�n mediante mysqli # Preparar las variables con los datos de conexi�n $host = 'localhost'; $usuario = 'root'; $clave = 'contrase�a'; $db = 'curso_php'; # Conectarse a la base de datos $conn = mysqli_connect($host, $usuario, $clave, $db); Preparar la consulta Preparar una consulta para trabajar mediante mysqli, requerir� de algunos cuantos pasos que debemos seguir, cuidada y ordenadamente. Primero, preparamos la sentencia. Pero a diferencia de la vez anterior (con mysql), los datos que relacionados con los registros (ya sean datos a insertar o coincidencias establecidas en la cl�usula where), no los pondremos. En su lugar, utilizaremos el comod�n ?: $sql = " INSERT INTO usuarios (username, email, password) VALUES (?, ?, ?) "; Luego, definiremos los datos din�micos mediante variables: $username = 'juan-perez'; $email = 'juan_perez@algundominio.ext'; $password = '26ec07ef61f135494b79a13674a9a4ae'; Luego, le indicamos a mysqli que inicie la preparaci�n para la consulta: 216 Programador PHP Experto Eugenia Bahit $pre = mysqli_prepare($conn, $sql); Y finalmente, le decimos que una los par�metros a la consulta, indic�ndole el tipo de datos correspondiente: mysqli_stmt_bind_param($pre, "sss", $username, $email, $password); El segundo par�metro de la fuci�n mysqli_stmt_bind_param (sss) indica el tipo de datos correspondiente a los par�metros (nuestras variables). La �s� significa string. Tres �s�, significan que los tres datos son de tipo string. Otros tipos de datos soportados son: (s) string (i) entero (d) doble/decimal (b) blob El equivalente para otros tipos de datos no listados arriba, es: Fechas y horas son cadenas de texto. Por lo tanto, se utiliza s Booleanos es un entero (O: False, 1: True). Utilizar i Ejecutar la consulta mysqli_stmt_execute($pre); Cerrar la consulta mysqli_stmt_close($pre); Cerrar la conexi�n mysqli_close($conn); 217 Programador PHP Experto Eugenia Bahit Ejemplo de inserci�n completo # Preparar las variables con los datos de conexi�n $host = 'localhost'; $usuario = 'root'; $clave = 'contrase�a'; $db = 'curso_php'; # Conectarse a la base de datos $conn = mysqli_connect($host, $usuario, $clave, $db); # Preparo la sentencia con los comodines ? $sql = " INSERT INTO usuarios (username, email, password) VALUES (?, ?, ?) "; # Preparo los datos que voy a insertar $username = 'juan-perez'; $email = 'juan_perez@algundominio.ext'; $password = '26ec07ef61f135494b79a13674a9a4ae'; # Preparo la consulta $pre = mysqli_prepare($conn, $sql); # indico los datos a reemplazar con su tipo mysqli_stmt_bind_param($pre, "sss", $username, $email, $password); # Ejecuto la consulta mysqli_stmt_execute($pre); # PASO OPCIONAL (SOLO PARA CONSULTAS DE INSERCI�N): # Obtener el ID del registro insertado $nuevo_id = mysqli_insert_id($conn); # Cierro la consulta y la conexi�n mysqli_stmt_close($pre); mysqli_close($conn); N�tese que el ejemplo de inserci�n, aplica tambi�n a consultas de actualizaci�n, modificaci�n y eliminaci�n. Capturar resultados de una consulta de selecci�n En una consulta de selecci�n (donde necesito capturar los 218 Programador PHP Experto Eugenia Bahit resultados devueltos), despu�s de ejecutar la consulta (y antes de cerrarla), al igual que hicimos con el conector mysql, vamos a capturar los resultados, pero de forma diferente. Primero, vamos a asociar la salida a variables. Esto es, asociar los nombres de los campos devueltos a nombres de variables y se logra as�: mysqli_stmt_bind_result($pre, $id, $username, $email); Claramente, le estamos indicando al conector, que nos referiremos a los campos id, username e email como variables $id, $username e $email respectivamente. Como a nostros nos interesa almacenar los datos en un array, a fin de continuar manteniendo nuestra arquitectura de aislaci�n del c�digo PHP y HTML, nuevamente, vamos a iterar sobre los resultados, para generar un array que almacene todos los registros devueltos: while(mysqli_stmt_fetch($pre)) { $registros[] = array('id'=>$id, 'username'=>$username, 'email'=>$email); } Como se puede ver, la diferencia es notable. Esta vez, tuvimos que encargarnos de crear el array pr�cticamente �a mano�. Ejemplo completo de consultas de selecci�n # Preparar las variables con los datos de conexi�n $host = 'localhost'; $usuario = 'root'; $clave = 'contrase�a'; $db = 'curso_php'; 219 Programador PHP Experto Eugenia Bahit # Conectarse a la base de datos $conn = mysqli_connect($host, $usuario, $clave, $db); # Preparo la sentencia con los comodines ? $sql = " SELECT id, email FROM usuarios WHERE username = ? AND password = ? "; # Preparo los datos que voy a insertar $username = 'juan-perez'; $password = '26ec07ef61f135494b79a13674a9a4ae'; # Preparo la consulta $pre = mysqli_prepare($conn, $sql); # indico los datos a reemplazar con su tipo mysqli_stmt_bind_param($pre, "ss", $username, $password); # Ejecuto la consulta mysqli_stmt_execute($pre); # asocio los nombres de campo a nombres de variables mysqli_stmt_bind_result($pre, $id, $email); # Capturo los resultados y los guardo en un array while(mysqli_stmt_fetch($pre)) { $registros[] = array('id'=>$id, 'email'=>$email); } # Cierro la consulta y la conexi�n mysqli_stmt_close($pre); mysqli_close($conn); 220 Programador PHP Experto Eugenia Bahit Introducci�n al Paradigma de la programaci�n orientada a objetos La orientaci�n a objetos es un paradigma de programaci�n que puede resultar complejo, si no se lo interpreta de forma correcta desde el inicio. Por eso, en esta primera parte, nos enfocaremos primero, en cuestiones de conceptos b�sicos, para luego, ir introduci�ndonos de a poco, en principios te�ricos elementalmente necesarios, para implementar la orientaci�n a objetos en la pr�ctica. 221 Programador PHP Experto Eugenia Bahit Pensar en objetos Pensar en objetos, puede resultar -al inicio- una tarea dif�cil. Sin embargo, dif�cil no significa complejo. Por el contrario, pensar en objetos representa la mayor simplicidad que uno podr�a esperar del mundo de la programaci�n. Pensar en objetos, es simple... aunque lo simple, no necesariamente signifique sencillo. Y �qu� es un objeto? Pues, como dije antes, es �simple�. Olvidemos los formalismos, la inform�tica y todo lo que nos rodea. Simplemente, olvida todo y conc�ntrate en lo que sigue. Lo explicar� de manera �simple�: Un objeto es �una cosa�. Y, si una cosa es un sustantivo, entonces un objeto es un sustantivo. Mira a tu alrededor y encontrar�s decenas, cientos de objetos. Tu ordenador, es un objeto. T�, eres un objeto. Tu llave es un objeto. El cenicero (ese que tienes frente a ti cargado de colillas de cigarrillo), es otro objeto. Tu mascota tambi�n es un objeto. Cuando pensamos en �objetos�, todos los sustantivos son objetos. Sencillo �cierto? Entonces, de ahora en m�s, solo conc�ntrate en pensar la vida en objetos (al menos, hasta terminar de leer este documento). 222 Programador PHP Experto Eugenia Bahit Ahora �qu� me dices si describimos las cualidades de un objeto? Describir un objeto, es simplemente mencionar sus cualidades. Las cualidades son adjetivos. Si no sabes que es un adjetivo, estamos jodidos (y mucho). Pero, podemos decir que un adjetivo es una cualidad del sustantivo. Entonces, para describir �la manera de ser� de un objeto, debemos preguntarnos �c�mo es el objeto? Toda respuesta que comience por �el objeto es�, seguida de un adjetivo, ser� una cualidad del objeto. Algunos ejemplos: � El objeto es verde � El objeto es grande � El objeto es feo Ahora, imagina que te encuentras frente a un ni�o de 2 a�os (ni�o: objeto que pregunta cosas que t� das por entendidas de forma impl�cita). Y cada vez que le dices las cualidades de un objeto al molesto ni�o-objeto, �ste te pregunta: -��Qu� es...?�, seguido del adjetivo con el cu�l finalizaste tu frase. Entonces, tu le respondes diciendo �es un/una� seguido de un sustantivo. Te lo muestro con un ejemplo: � El objeto es verde. �Qu� es verde? Un color. � El objeto es grande. �Qu� es grande? Un tama�o. � El objeto es feo. �Qu� es feo? Un aspecto. Estos sustantivos que responden a la pregunta del ni�o, 223 Programador PHP Experto Eugenia Bahit pueden pasar a formar parte de una locuci�n adjetiva que especifique con mayor precisi�n, las descripciones anteriores: � El objeto es de color verde. � El objeto es de tama�o grande. � El objeto es de aspecto feo. Podemos decir entonces -y todo esto, gracias al molesto ni�oobjeto-, que una cualidad, es un atributo (derivado de � cualidad a tribuible a un objeto�) y que entonces, un objeto es un sustantivo que posee atributos, cuyas cualidades lo describen. Ve�moslo m�s gr�ficamente: OBJETO (sustantivo) ATRIBUTO (locuci�n adjetiva) CUALIDAD DEL ATRIBUTO (adjetivo) (el) Objeto (es de) color Verde (es de) tama�o Grande (es de) aspecto Feo Pero algunos objetos, tambi�n se componen de otros objetos... Adem�s de cualidades (locuci�n adjetiva seguida de un adjetivo), los objetos �tienen otras cosas�. Estas �otras cosas�, son aquellas �pseudo-cualidades� que en vez de responder a �c�mo es el objeto? responden a ��c�mo est� compuesto el objeto?� o incluso, a�n m�s simple ��Qu� tiene el objeto?�. La respuesta a esta pregunta, estar� dada por la frase �el objeto tiene...�, seguida de un adverbio de cantidad (uno, varios, muchos, algunos, unas cuantas) y un sustantivo. 224 Programador PHP Experto Eugenia Bahit Algunos ejemplos: � El objeto tiene algunas antenas � El objeto tiene un ojo � El objeto tiene unos cuantos pelos Los componentes de un objeto, tambi�n integran los atributos de ese objeto. Solo que estos atributos, son algo particulares: son otros objetos que poseen sus propias cualidades. Es decir, que estos �atributos-objeto� tambi�n responder�n a la pregunta ��C�mo es/son ese/esos/esas?� seguido del atributoobjeto (sustantivo). Ampliemos el ejemplo para que se entienda mejor: � El objeto tiene algunas antenas. �C�mo son esas antenas? � Las antenas son de color violeta � Las antenas son de longitud extensa � El objeto tiene un ojo. �C�mo es ese ojo? � El ojo es de forma oval � El ojo es de color azul � El ojo es de tama�o grande � El objeto tiene unos cuantos pelos. �C�mo son esos pelos? � Los pelos son de color fucsia � Los pelos son de textura rugosa 225 Programador PHP Experto Eugenia Bahit Pong�moslo m�s gr�fico: OBJETO (sustantivo) ATRIBUTO-OBJETO (sustantivo) ATRIBUTOS (locuci�n adjetiva) CUALIDADES DE LOS ATRIBUTOS (adjetivo) (el) Objeto (tiene algunas) antenas (de) color (de) longitud Violeta extensa (tiene un) ojo (de) forma (de) color (de) tama�o Oval azul grande (tiene unos cuantos) pelos (de) color (de) textura Fucsia rugosa Entonces, podemos deducir que un objeto puede tener dos tipos de atributos: 1) Los que responden a la pregunta ��C�mo es el objeto?� con la frase �El objeto es...� + adjetivo (atributos definidos por cualidades) 2) Los que responden a la pregunta ��Qu� tiene el objeto?� con la frase �El objeto tiene...� + sustantivo (atributos definidos por las cualidades de otro objeto) Ve�moslo a�n, m�s gr�ficamente: 226 Programador PHP Experto Eugenia Bahit Viendo el gr�fico anterior, tenemos lo siguiente: Un objeto (sustantivo) al cual hemos descrito con tres atributos (adjetivos) y otros tres atributos-objeto (sustantivos) los cu�les son a la vez, otros tres objetos (sustantivos) con sus atributos (adjetivos) correspondientes. �Simple, no? Ahora, compliquemos todo un poco. Y tambi�n hay objetos que comparten caracter�sticas con otros objetos Resulta ser, que nuestro Objeto, es pr�cticamente igual a un nuevo objeto. Es decir, que el nuevo objeto que estamos viendo, tiene absolutamente todas las caracter�sticas que nuestro primer objeto, es decir, tiene los mismos atributos. Pero tambi�n, tiene algunas m�s. Por ejemplo, este nuevo objeto, adem�s de los atributos de nuestro primer objeto, tiene un pie. Es decir, que las caracter�sticas de nuestro 227 Objeto color tama�o aspecto antenas ojos pelos Antena color longitud Pelo color textura forma color tama�o Ojo Programador PHP Experto Eugenia Bahit nuevo objeto, ser�n todas las del objeto original, m�s una nueva: pie. Repasemos las caracter�sticas de nuestro nuevo objeto: � El nuevo objeto es de color verde. � El nuevo objeto es de tama�o grande. � El nuevo objeto es de aspecto feo. � El nuevo objeto tiene algunas antenas. �C�mo son esas antenas? � Las antenas son de color violeta � Las antenas son de longitud extensa � El nuevo objeto tiene un ojo. �C�mo es ese ojo? � El ojo es de forma oval � El ojo es de color azul � El ojo es de tama�o grande � El nuevo objeto tiene unos cuantos pelos. �C�mo son esos pelos? � Los pelos son de color fucsia � Los pelos son de textura rugosa (nuevas caracter�sticas) � El nuevo objeto tiene un pie. �C�mo es ese pie? � El pie es de forma rectangular 228 Programador PHP Experto Eugenia Bahit � El pie es de color amarillo � El pie tiene 3 dedos. �C�mo son esos dedos? � Los dedos son de longitud mediana � Los dedos son de forma alargada � Los dedos son de color amarillo Veamos todas las caracter�sticas de este nuevo, en un gr�fico como lo hicimos antes. Con mucha facilidad, podemos observar como nuestro nuevo objeto es una especie de �objeto original ampliado�. Es decir 229 forma color dedos Dedo longitud forma color Nuevo Objeto Pie Pie Objeto color tama�o aspecto antenas ojos pelos Antena color longitud Pelo color textura forma color tama�o Ojo Programador PHP Experto Eugenia Bahit que el nuevo objeto, es exactamente igual al objeto original (comparte todos sus atributos) pero posee nuevas caracter�sticas. Est� claro adem�s, que el objeto original y el nuevo objeto, son dos objetos diferentes �cierto? No obstante, el nuevo objeto es un sub-tipo del objeto original. Ahora s�, a complicarnos a�n m�s. Los objetos, tambi�n tienen la capacidad de �hacer cosas� Ya describimos las cualidades de nuestros objetos. Pero de lo que no hemos hablado, es de aquellas cosas que los objetos �pueden hacer�, es decir, �cu�les son sus capacidades�. Los objetos tiene la capacidad de realizar acciones. Las acciones, son verbos. Es decir, que para conocer las capacidades de un objeto, debes preguntarte ��Qu� puede hacer el objeto?� y la respuesta a esta pregunta, estar� dada por todas aquellas que comiencen por la frase �el objeto puede� seguida de un verbo en infinitivo. Algunos ejemplos: � El objeto original puede flotar � El nuevo objeto (adem�s) puede saltar Si completamos el gr�fico anterior con las acciones, obtendremos lo siguiente: 230 Programador PHP Experto Eugenia Bahit Si observas el gr�fico anterior, notar�s que el nuevo objeto, no solo tiene los mismos atributos que el objeto original, sino que adem�s, tambi�n puede realizar las mismas acciones que �ste. Sencillo, cierto? Ahora s�, compliqu�monos del todo :) Objetos y m�s objetos: la parte dif�cil Si entendiste todo lo anterior, ahora viene la parte dif�cil. �Viste que esto de �pensando en objetos� viene a colaci�n de la programaci�n orientada a objetos? Bueno, la parte dif�cil es 231 forma color dedos Dedo longitud forma color Nuevo Objeto Pie Pie Objeto color tama�o aspecto antenas ojos pelos Antena color longitud Pelo color textura forma color tama�o Ojo Programador PHP Experto Eugenia Bahit que en la programaci�n, todo lo que acabamos de ver, se denomina de una forma particular. Pero, la explicaci�n es la misma que te di antes. Al pan, pan. Y al vino, vino. Las cosas por su nombre Cuando en el documento... En la programaci�n se denomina... Y con respecto a la programaci�n orientada a objetos es... Hablamos de �objeto� Objeto Un elemento Hablamos de �atributos� (o cualidades) Propiedades Un elemento Hablamos de �acciones� que puede realizar el objeto M�todos Un elemento Hablamos de �atributosobjeto� Composici�n Una t�cnica Vemos que los objetos relacionados entre s�, tienen nombres de atributos iguales (por ejemplo: color y tama�o) y sin embargo, pueden tener valores diferentes Polimorfismo Una caracter�stica Hablamos de objetos que son sub-tipos (o ampliaci�n) de otros Herencia Una caracter�stica Ahora, pasemos a un marco un poco m�s �acad�mico�. 232 Programador PHP Experto Eugenia Bahit Programaci�n Orientada a Objetos La Programaci�n Orientada a Objetos (POO u OOP por sus siglas en ingl�s), es un paradigma de programaci�n. Paradigma: �Teor�a cuyo n�cleo central [...] suministra la base y modelo para resolver problemas [...] � Definici�n de la Real Academia Espa�ola, vig�simo tercera edici�n C�mo tal, nos ense�a un m�todo -probado y estudiado- el cual se basa en las interacciones de objetos (todo lo descrito en el t�tulo anterior, �Pensar en objetos�) para resolver las necesidades de un sistema inform�tico. B�sicamente, este paradigma se compone de 6 elementos y 7 caracter�sticas que veremos a continuaci�n. Elementos y Caracter�sticas de la POO Los elementos de la POO, pueden entenderse como los �materiales� que necesitamos para dise�ar y programar un sistema, mientras que las caracter�sticas, podr�an asumirse como las �herramientas� de las cu�les disponemos para construir el sistema con esos materiales. Entre los elementos principales de la POO, podremos 233 Programador PHP Experto Eugenia Bahit encontrar a: Clases Las clases son los modelos sobre los cu�les se construir�n nuestros objetos. Podemos tomar como ejemplo de clases, el gr�fico que hicimos en la p�gina 8 de este documento. En PHP, una clase se define con la instrucci�n class seguida de un nombre gen�rico para el objeto. class Objeto { } class Antena { } class Pelo { } class Ojo { } Sugerencia de estilos El nombre de las clases se define en singular, utilizando CamelCase. Propiedades Las propiedades, como hemos visto antes, son las caracter�sticas intr�nsecas del objeto. �stas, se representan a modo de variables, solo que t�cnicamente, pasan a denominarse �propiedades�: class Antena { public $color = ""; public $longitud = ""; } class Pelo { public $color = ""; public $textura = ""; 234 Programador PHP Experto Eugenia Bahit } class Ojo { public $forma = ""; public $color = ""; public $tamanio = ""; } class Objeto { public $color = ""; public $tamanio = ""; public $aspecto = ""; public $antenas = array(); # ser� otro objeto public $ojos = array(); # ser� otro objeto public $pelos = array(); # ser� otro objeto } Estilos: Las propiedades se definen de la misma forma que las variables (aplican las mismas reglas de estilo). M�todos Los m�todos son �funciones� (como las que utilizamos en la programaci�n estructurada), solo que t�cnicamente se denominan m�todos, y representan acciones propias que puede realizar el objeto (y no otro): class Objeto { public $color = ""; public $tamanio = ""; public $aspecto = ""; public $antenas = array(); # ser� otro objeto public $ojos = array(); # ser� otro objeto public $pelos = array(); # ser� otro objeto function flotar() { } } 235 Programador PHP Experto Eugenia Bahit Objeto Las clases por s� mismas, no son m�s que modelos que nos servir�n para crear objetos en concreto. Podemos decir que una clase, es el razonamiento abstracto de un objeto, mientras que el objeto, es su materializaci�n. A la acci�n de crear objetos, se la denomina �instanciar una clase� y dicha instancia, consiste en asignar la clase, como valor a una variable: class Objeto { public $color = ""; public $tamanio = ""; public $aspecto = ""; public $antenas = array(); # ser� otro objeto public $ojos = array(); # ser� otro objeto public $pelos = array(); # ser� otro objeto function flotar() { } } $et = new Objeto() print $et->color; print $et->tamanio; print $et->aspecto; $et->color = "rosa"; print $et->color; Herencia: caracter�stica principal de la POO Como comentamos en el t�tulo anterior, algunos objetos comparten las mismas propiedades y m�todos que otro objeto, y adem�s agregan nuevas propiedades y m�todos. A esto se lo denomina herencia: una clase que hereda de otra. # NuevoObjeto hereda de otra clase: Objeto class NuevoObjeto extends Objeto { public $pie = array(); function saltar() { } } 236 Programador PHP Experto Eugenia Bahit Accediendo a los m�todos y propiedades de un objeto Una vez creado un objeto, es decir, una vez hecha la instancia de clase, es posible acceder a sus m�todos y propiedades. Para ello, PHP utiliza una sintaxis muy simple: el nombre del objeto, seguido de �->� y la propiedad o m�todo al cu�l se desea acceder: $objeto = new MiClase() print $objeto->propiedad; $objeto->otra_propiedad = "Nuevo valor"; $variable = $objeto->metodo(); print $variable print $objeto->otro_metodo(); Acceder a las propiedades de un objeto, dentro de la clase Para poder acceder a las propiedades del objeto, dentro de la clase, se utiliza la pseudo variable $this y lo mismo aplica para acceder a los m�todos: class A { public $foo = ''; function bar() { $this->foo = 'Hola Mundo'; $this->foobar(); } function foobar() { print $this->foo; } } 237 Programador PHP Experto Eugenia Bahit Composici�n Como comentamos anteriormente, algunos objetos se componen de las propiedades de otro (lo cual, no significa que las hereden, sino simplemente eso: �se componen de�). Cuando la propiedad de un objeto, se compone de las caracter�sticas de otro objeto, dicha propiedad se transforma en una especie de �propiedad-objeto�. Es decir, que el tipo de datos de esta propiedad, pasa a ser de tipo objeto. Esto significa, que dicha propiedad, estar� formada por subpropiedades. Para lograrlo, debemos recurrir a la composici�n. Una t�cnica que en PHP, se logra creando un m�todo que asigne a dicha propiedad, el objeto correspondiente, pasado como par�metro: class Objeto { public $color = ""; public $tamanio = ""; public $aspecto = ""; public $antenas = array(); # ser� otro objeto public $ojos = array(); # ser� otro objeto public $pelos = array(); # ser� otro objeto function set_antena(Antena $antena) { $this->antenas[] = $antena; } function set_ojo(Ojo $ojo) { $this->ojos[] = $ojo; } function set_pelo(Pelo $pelo) { $this->pelos[] = $pelos; } } 238 Programador PHP Experto Eugenia Bahit Notar que al par�metro se le indica previamente que deber� ser un objeto de tipo Antena, Ojo y Pelo respectivamente. 239 Programador PHP Experto Eugenia Bahit Ejemplo pr�ctico de Herencia vs. Composici�n Como comentamos anteriormente, la diferencia entre la herencia y la composici�n es radical. Mientras que en el primer caso, un objeto pasa a ser un subtipo del objeto del cu�l hereda, en el segundo, ambos objetos pueden ser sumamente diferentes entre s� y no guardar ninguna caracter�stica en com�n. El siguiente ejemplo lo demuestra claramente: <?php class Categoria { public $nombre = ""; } class Producto { public $descripcion = ""; public $categoria = array(); public $precio = 0.0; public function __construct(Categoria $categoria) { $this->categoria = $categoria; } } class PrendaDeVestir extends Producto { public $marca = ""; public $talle = ""; public $color = ""; public $material = ""; } class Libro extends Producto { public $titulo = ""; public $autor = ""; public $editorial = ""; public $paginas = 0; public $encuadernacion = ""; public $anio_edicion = 0; } $categoria = new Categoria(); $categoria->nombre = "Vestimenta"; $pantalon = new PrendaDeVestir($categoria); $pantalon->descripcion = "Pantal�n cl�sico recto"; $pantalon->marca = "Christian Dior"; $pantalon->talle = "XS"; $pantalon->color = "Negro"; 240 Programador PHP Experto Eugenia Bahit $pantalon->material = "Lino"; $pantalon->precio = 978.50; print <<<EOT ===================================================================== DATOS DEL PRODUCTO ===================================================================== CATEGOR�A: {$pantalon->categoria->nombre} PRODUCTO: {$pantalon->descripcion} MARCA: {$pantalon->marca} TALLE: {$pantalon->talle} COLOR: {$pantalon->color} MATERIAL: {$pantalon->material} PRECIO: \$ {$pantalon->precio}.- EOT; ?> 241 Programador PHP Experto Eugenia Bahit Programando con Historias de Usuario Una historia de usuario es aquella que puede escribirse con la siguiente frase: Como [un usuario], puedo [acci�n/funcionalidad] para [beneficio] Por ejemplo: Como administrador del sistema, puedo agregar productos al cat�logo para ser visualizados por los clientes . Muchas veces, puede resultar redundante o hasta incluso carecer de sentido, indicar el beneficio. Por ello, es frecuente describir las historias de usuario, sin incorporar este tercer elemento: Como administrador del sistema, puedo agregar productos al cat�logo. Vale aclarar, que es frecuente encontrar t�rminos como �quiero� o �necesito� en reemplazo de �puedo� cuando se describen las historias de usuario: 242 Programador PHP Experto Eugenia Bahit Criterios de Aceptaci�n Es recomendable que cada Historia de Usuario, especifique cuales son los criterios de aceptaci�n, para considerar cumplido el requisito. Los criterios de aceptaci�n, entonces, no son m�s que �pautas� o peque�as �reglas� que una historia de usuario debe respetar para considerarla cumplida. Por ejemplo, para la historia de usuario �Como administrador del sistema necesito agregar productos al cat�logo� los criterios de aceptaci�n, podr�an ser: 243 Programador PHP Experto Eugenia Bahit � Cada producto debe contener: � c�digo de producto (opcional), � descripci�n de hasta 500 caracteres (opcional) � precio (obligatorio) � stock inicial (opcional) � un nombre (obligatorio), � una foto (opcional) � No pueden existir dos productos con el mismo nombre de producto o c�digo de producto � El nombre del producto jam�s puede estar vac�o o tener menos de 3 caracteres � Cuando no se asigne un stock inicial al producto, �ste debe asignarse autom�ticamente en cero (sin stock) Dividiendo Historias de Usuario en Tareas La estrategia consiste en desmembrar el item a la m�nima expresi�n posible, encuadrada en un mismo tipo de actividad. El desmembramiento debe hacerse "de lo general a lo particular, y de lo particular al detalle". Historia de Usuario # 123 Como usuario puedo ingresar mi e-mail y contrase�a para acceder al sistema Prioridad 5 Valor 244 Programador PHP Experto Eugenia Bahit Criterios de aceptaci�n: 100 Esfuerzo 21 Ficha t�pica de Historia de Usuario An�lisis General: Es aquel que responde a la pregunta �qu� es? � un sistema de validaci�n de usuarios registrados An�lisis Particular: Es el que responde a la pregunta �c�mo hacerlo? � Arquitectura MVC (requiere hacer el modelo, la l�gica y la GUI de la vista y el controlador) An�lisis detallado: Es el que responde a la pregunta general �qu� tareas se necesitan hacer para lograrlo? Los detalles, son aquellas restricciones que deber�n considerarse para todo lo anterior. Por ejemplo, la creaci�n del modelo, repercutir� en la base de datos. Por lo cual, tras crear los nuevos modelos, habr� que correr el ORM para que modifique las tablas. 245 Programador PHP Experto Eugenia Bahit Otro detalle a considerar, es el tiempo que demanda cada tarea. Por ejemplo, correr un ORM lleva solo algunos minutos, pues no puede ser considerado una �nica tarea. Entonces, puede "sumarse como detalle" a la tarea "crear modelos". De manera contraria, documentar en el manual del usuario, llevar� todo un d�a de trabajo. Por lo cual, debe asignarse a una �nica tarea. � Crear el modelo Usuario y correr el ORM para modificar las tablas � Tag: programaci�n � Esfuerzo: 2 h � Dise�ar un formulario HTML para insertar usuario y contrase�a � Tag: dise�o � Esfuerzo: 4 h � Desarrollar la l�gica de la vista del formulario de logueo � Tag: programaci�n � Esfuerzo: 4 h � Crear el controlador para el modelo � Tag: programaci�n � Esfuerzo: 6 h � Correr los test e integrar � Tag: testing � Esfuerzo: 1 h 246 Programador PHP Experto Eugenia Bahit Introducci�n a la Programaci�n eXtrema eXtreme Programming (programaci�n extrema) tambi�n llamado XP, es una metodolog�a de trabajo que tiene su origen en 1996, de la mano de Kent Beck, Ward Cunningham y Ron Jeffries. XP propone un conjunto de 12 pr�cticas t�cnicas, que aplicadas de manera simult�nea, pretenden enfatizar los efectos positivos de en un proyecto de desarrollo de Software. En este curso, nos enfocaremos en tres de ellas: 1. TDD 2. Integraci�n continua 3. Refactoring 247 Programador PHP Experto Eugenia Bahit TDD � Test-Driven Development Entre las pr�cticas t�cnicas sugeridas por XP, nos encontramos con la t�cnica de programaci�n TDD, del ingl�s Test-Driven Developmen (Desarrollo Guiado por Pruebas). A muchos asusta esta pr�ctica por el simple hecho de ser �desconocida� y resultar su descripci�n, algo confusa: �Qu� es a caso, aquello de hacer un test antes de programar? Pero no dejes que el miedo a lo desconocido te gane, que aprender a programar gui�ndote por test, es algo realmente simple, divertido y sobre todo, muy productivo. �Qu� es el desarrollo -o programaci�n- guiado por pruebas? TDD es una t�cnica de programaci�n que consiste en guiar el desarrollo de una aplicaci�n, por medio de Test Unitarios. Los Test Unitarios (Unit Testing) no son m�s que algoritmos que emulan lo que la aplicaci�n se supone deber�a hacer, convirti�ndose as�, en un modo simple de probar que lo que �piensas� programar, realmente funciona. 248 Programador PHP Experto Eugenia Bahit Para verlo de forma sencilla, imagina que est�s desarrollando una aplicaci�n, que necesita un algoritmo que sume dos n�meros. Sabes perfectamente como hacerlo: function sumar_dos_numeros($a, $b) { return $a + $b; } $a = 10; $b = 25; $suma = sumar_dos_numeros($a, $b); echo $suma; // salida: 35 �Sencillo verdad? �Para qu� necesitar�as complicarte la vida con eso de los test? function sumar_dos_numeros($a, $b) { return $a + $b; } $a = 'Zanahorias: 10'; $b = '25 hinojos; $suma = sumar_dos_numeros($a, $b); echo $suma; // salida: 25 �a caso 10 zanahorias m�s 25 hinojos no deber�a dar como resultado, 35 vegetales? Hasta que no lo pruebas, tal vez no sepas que suceda. Y hasta no saber que sucede, no sabr�s como solucionarlo. Y para esto, sirven los Test, ya que te guiar�n para saber qu�, c�mo, cu�les y cu�ntos algoritmos necesitar�s desarrollar para que tu aplicaci�n haga lo que se supone debe hacer. Es entonces, que en el caso anterior, los test que se desarrollen previamente, ir�n guiando tu desarrollo hasta encontrar la soluci�n a todos los problemas. Y siguiendo el ejemplo, sabr�s 249 Programador PHP Experto Eugenia Bahit que evidentemente, necesitar�s de alg�n algoritmo extra: validas los par�metros antes de sumarlos y, o los conviertes a enteros o retornas un mensaje de error. En definitiva, los Test Unitarios ser�n una gu�a para entender como funciona el c�digo, ayud�ndote a organizarlo de manera clara, legible y simple. Carlos Bl� Jurado4 en su libro Dise�o �gil con TDD5 nos define al TDD como: �[...] la respuesta a las grandes preguntas: �C�mo lo hago? �Por d�nde empiezo? �C�mo se qu� es lo que hay que implementar y lo que no? �C�mo escribir un c�digo que se pueda modificar sin romper funcionalidad existente? [...]� Seg�n Kent Beck -uno de los principales fundadores de la metodolog�a XP-, implementar TDD nos otorga grandes ventajas: 1. La calidad del software aumenta disminuyendo pr�cticamente a cero, la cantidad de bugs en la aplicaci�n; 2. Conseguimos c�digo altamente reutilizable puesto que los test nos obligan a desarrollar algoritmos gen�ricos; 3. El trabajo en equipo se hace m�s f�cil, une a las personas, ya que al desarrollar con test, nos 4 Carlos Bl� es un desarrollador de Software y emprendedor espa�ol, con una gran trayectoria en la ingenier�a de sistemas �giles. Visitar su blog: www.carlosble.com 5 Dise�o �gil con TDD. ISBN 978-1-4452-6471-4. Creative Commons-ND 250 Programador PHP Experto Eugenia Bahit aseguramos no romper funcionalidades existentes de la aplicaci�n; 4. Nos permite confiar en nuestros compa�eros aunque tengan menos experiencia. Esto es, debido a que el hecho de tener que desarrollar test antes de programar el algoritmo definitivo, nos asegura -independientemente del grado de conocimiento y experiencia del desarrollador- que el algoritmo efectivamente, har� lo que se supone, debe hacer y sin fallos; 5. Escribir el ejemplo (test) antes que el c�digo nos obliga a escribir el m�nimo de funcionalidad necesaria, evitando sobredise�ar, puesto que desarrollando lo m�nimamente indespensable, se obtiene un panorama m�s certero de lo que la aplicaci�n hace y cu�l y c�mo es su comportamiento interno; 6. Los tests son la mejor documentaci�n t�cnica que podemos consultar a la hora de entender qu� misi�n cumple cada pieza del rompecabezas, ya que cada test, no es m�s que un �caso de uso� traducido en idioma inform�tico. Test Unitarios Los Test Unitarios (o Unit Testing), representan el alma de la programaci�n dirigida por pruebas. Son test que se encargan de verificar -de manera simple y r�pida- el comportamiento de una parte m�nima de c�digo, de forma independiente y sin alterar el funcionamiento de otras partes de la aplicaci�n. 251 Programador PHP Experto Eugenia Bahit Caracter�sticas de los Test Unitarios Un Test Unitario posee cuatro caracter�sticas particulares que debe guardar para considerarse �unitario�. Estas cualidades son: 1. At�mico: Prueba una parte m�nima de c�digo. Dicho de manera simple, cada test unitario debe probar una -y solo una- �acci�n� realizada por un m�todo. Por ejemplo, para un m�todo que retorna el neto de un monto bruto m�s el IVA correspondiente, deber� haber un test que verifique recibir en forma correcta el importe bruto, otro que verifique el c�lculo del IVA sobre un importe bruto y finalmente, un tercer test unitario que verifique el c�lculo de un importe bruto m�s su IVA correspondiente. 2. Independiente: Cada Test Unitario DEBE ser independiente de otro. Por ejemplo, siguiendo el caso anterior, el test que verifique la suma de un importe bruto m�s su IVA correspondiente, no debe depender del test que verifica el c�lculo del IVA. 3. Inocuo: Podr�a decirse que cada test unitario debe ser inofensivo para el Sistema. Un test unitario DEBE poder correrse sin alterar ning�n elemento del sistema, es decir, que no debe, por ejemplo, agregar, editar o eliminar registros de una base de datos. 252 Programador PHP Experto Eugenia Bahit 4. R�pido: La velocidad de ejecuci�n de un test unitario cumple un papel fundamental e ineludible en el desarrollo guiado por pruebas, ya que de la velocidad de ejecuci�n de un test, depender� de manera proporcional, la velocidad con la que una funcionalidad se desarrolle. Anatom�a Los Test Unitarios se realizan, en cualquier lenguaje de programaci�n, mediante herramientas -Frameworks- con un formato determinado, conocido como xUnit. De all�, que los frameworks para Unit Testing que cumplen con dicho formato, suelen tener nombres compuestos por una abreviatura del lenguaje de programaci�n, seguida del t�rmino �unit�: PyUnit (Python), PHPUnit (PHP), ShUnit (Shell Scripting), CppUnit (C++), etc. Exceptuando el caso de Shell Scripting, los frameworks xUnit, utilizan el paradigma de programaci�n orientada a objetos (OOP) tanto en su anatom�a de desarrollo como para su implementaci�n (creaci�n de test unitarios). Por lo tanto, los Test Unitarios se agrupan en clases, denominadas Test Case, que heredan de una clase del framework xUnit, llamada xTestCase: class BalanceContableTest extends PHPUnit_Framework_TestCase { # M�todos } Creaci�n de una clase Test Case con PHPUnit (PHP). La clase hereda de PHPUnit_Framework_TestCase 253 Programador PHP Experto Eugenia Bahit Los m�todos contenidos en una clase Test Case, pueden o no, ser Test Unitarios. Los Test Unitarios contenidos en una clase Test Case, deben contener el prefijo test_ en el nombre del m�todo a fin de que el framework los identifique como tales. ini_set('include_path', '.:/usr/share/php:/usr/share/pear'); class BalanceContableTest extends PHPUnit_Framework_TestCase { public function test_calcular_iva() { # Algoritmo } } Definici�n de un m�todo �test� con PHPUnit. N�tese que los m�todos de una clase Test Case DEBEN ser m�todos p�blicos. Otra ventaja que los frameworks xUnit nos proveen, es la facilidad de poder crear dos m�todos especiales dentro de una clase Test Case -que no son test-, los cuales est�n destinados a preparar el escenario necesario para correr los test de esa clase y eliminar aquello que se desee liberar, una vez que el test finalice. Estos m�todos, son los denominados setUp() y tearDown() respectivamente: class BalanceContableTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->importe_bruto = 100; $this->alicuota_iva = 21; } public function tearDown() { $this->importe_bruto = 0; $this->alicuota_iva = 0; } public function test_calcular_iva() { # Algoritmo } } Los m�todos setUp() y tearDown() en PHPUnit 254 Programador PHP Experto Eugenia Bahit Esta anatom�a dual -por un lado, la del Framework y por otro, la de utilizaci�n o implementaci�n de �ste-, se lograr� finalmente, dividiendo a cada Test Case -imaginariamente- en tres partes, identificadas por las siglas AAA las cu�les representan a las tres �acciones� que son necesarias llevar a cabo, para dar forma a los Tests: Arrange, Act and Assert (preparar, actuar y afirmar) Preparar los test consiste en establecer -setear o configurartodo aquello que sea necesario para que cada uno de los m�todos �test� pueda ejecutarse. Esto puede ser, la declaraci�n de propiedades comunes a todos los test, la instancia a objetos, etc. Cuando estas �preparaciones previas� sean comunes a todos los test, deber�n valerse del m�todo setUp() para ser creadas. De lo contrario, se crear�n m�todos -a modo de helpers- dentro de la clase Test Case, a los cu�les cada uno de los test, recurra cuando los necesite. Vale aclarar que estos �helpers� NO podr�n contener el prefijo �test� en su nombre. Actuar, se refiere a hacer la llamada al c�digo del Sistema cubierto por Test (SUT) que se desea probar. Esto se conoce como �cobertura de c�digo� o Code Coverage: class BalanceContableTest extends PHPUnit_Framework_TestCase { # Arrange (preparar) public function setUp() { // importar la clase a ser testeada require_once('/contabilidad/models/balance_contable.php'); 255 Programador PHP Experto Eugenia Bahit // setear propiedades comunes $this->importe_bruto = 100; $this->alicuota_iva = 21; } public function tearDown() { $this->importe_bruto = 0; $this->alicuota_iva = 0; } public function test_calcular_iva() { # Act (actuar) // Instanciar al objeto que ser� probado $this->coverage = new BalanceContable(); // modificar las propiedades del objeto $this->coverage->importe_bruto = $this->importe_bruto; $this->coverage->alicuota_iva = $this->alicuota_iva; // invocar al m�todo que se est� testeando $result = $this->coverage->calcular_iva(); # Assert (afirmar) // sentencias } } Finalmente, afirmar el resultado de un test, se refiere a invocar a los m�todos assert del Framework xUnit, que sean necesarios para afirmar que el resultado obtenido durante la actuaci�n, es el esperado (m�s adelante, veremos cu�les son estos m�todos assert de los cuales disponemos): class BalanceContableTest extends PHPUnit_Framework_TestCase { # Arrange (preparar) public function setUp() { // importar la clase a ser testeada require_once('/contabilidad/models/balance_contable.php'); // setear propiedades comunes $this->importe_bruto = 100; $this->alicuota_iva = 21; } public function tearDown() { 256 Programador PHP Experto Eugenia Bahit $this->importe_bruto = 0; $this->alicuota_iva = 0; } public function test_calcular_iva() { # Act (actuar) // Instanciar al objeto que ser� probado $this->coverage = new BalanceContable(); // modificar las propiedades del objeto $this->coverage->importe_bruto = $this->importe_bruto; $this->coverage->alicuota_iva = $this->alicuota_iva; // invocar al m�todo que se est� testeando $result = $this->coverage->calcular_iva(); # Assert (afirmar) $this->assertEquals(121, $result); } } Algoritmo para escribir pruebas unitarias Existe un algoritmo (o por qu� no, �una f�rmula�), para escribir Test Unitarios bajo TDD, el cual consiste en: PRIMER PASO: Escribir el Test y hacer que falle Para ello, lo primero que haremos ser� crear nuestra clase Test Case (n�tese que ning�n otro c�digo ha sido escrito al momento, para esta aplicaci�n). Nos baseremos en el mismo ejemplo que venimos siguiendo, donde lo que se necesita es calcular el IVA a un monto bruto dado. <?php require_once('contabilidad/models/BalanceContable.php'); class BalanceContableTest extends PHPUnit_Framework_TestCase { public function test_calcular_iva() { 257 Programador PHP Experto Eugenia Bahit $this->coverage = new BalanceContable(); $this->coverage->importe_bruto = 1500; $this->coverage->alicuota_iva = 21; $result = $this->coverage->calcular_iva(); $this->assertEquals(315, $result); } } ?> archivo: /MyApp/Tests/BalanceContableTest.php Nuestro test, nos est� guiando el desarrollo, dici�ndonos que: 1. Debemos crear una clase llamada BalanceContable que la guardaremos en el archivo BalanceContable.php 2. Esta clase debe tener dos propiedades: importe_bruto y alicuota_iva 3. La clase debe contener un m�todo llamado calcular_iva() �Qu� hace nuestro test? Verificar� que el m�todo calcular_iva() retorne el valor 315. Este valor, debe corresponder al 21% (alicuota_iva) de 1500 (importe_bruto). Entonces, el siguiente paso, ser� crear aquello que nuestro Test, nos gui� a hacer: <?php class BalanceContable { public $importe_bruto; public $alicuota_iva; public function calcular_iva() { 258 Programador PHP Experto Eugenia Bahit } } ?> archivo: /MyApp/contabilidad/models/BalanceContable.php Finalmente, correremos nuestro Test, y �ste, deber� fallar: eugenia@cocochito:~/borrador/MyApp$ phpunit Tests PHPUnit 3.4.5 by Sebastian Bergmann. F Time: 0 seconds, Memory: 4.00Mb There was 1 failure: 1) BalanceContableTest::test_calcular_iva Failed asserting that <null> matches expected <integer:315>. /home/eugenia/borrador/MyApp/Tests/BalanceContableTest.php:11 FAILURES! Tests: 1, Assertions: 1, Failures: 1. En la muestra de consola anterior, corrimos el Test creado mediante el comando phpunit al cual le pasamos como par�metro la carpeta Tests que contiene (y contendr�) todos nuestros test. Se resaltan en color naranja, las explicaciones arrojadas por PHPUnit, donde nos dice que el m�todo test_calcular_iva del Test Case BalanceContableTest ha fallado, en afirmar que NULL coincidi� con el valor 315 esperado de tipo entero. Con lo anterior, nos aseguramos que el Test falle, cuando se espera que as� sea. SEGUNDO PASO: Escribir la m�nima cantidad de c�digo para que el test pase. De la misma forma que nos aseguramos que el test fallar� cuando deba hacerlo, ahora debemos asegurarnos de que el test NO falle cuando no lo esperamos. 259 Programador PHP Experto Eugenia Bahit Para ello, debemos evitar escribir -de buenas a primeras-, el algoritmo de nuestro m�todo. Por el contrario, debemos encontrar la expresi�n m�nima que nos retorne el resultado que esperamos. Es decir, que nos retorne el entero 315. Entonces, editamos nuestra clase BalanceContable y agregamos la m�nima expresi�n necesaria al m�todo calcular_iva(): <?php class BalanceContable { public $importe_bruto; public $alicuota_iva; public function calcular_iva() { return 315; } } ?> Volvemos a correr el test para asegurarnos de que esta vez, NO falle: eugenia@cocochito:~/borrador/MyApp$ phpunit Tests PHPUnit 3.4.5 by Sebastian Bergmann. . Time: 0 seconds, Memory: 4.00Mb OK (1 test, 1 assertion) TERCER PASO: Escribir un nuevo test y hacer que falle Hemos escrito la cantidad m�nima de c�digo para que nuestro primer test, pase. Lo hemos hecho fallar, cuando a�n no exist�a algoritmo o instrucci�n en el m�todo probado. Es hora de crear un nuevo test que falle, cuando ya existe una m�nima porci�n de c�digo escrita (o modificar el existe, cambiando los par�metros de �ste). Agregaremos entonces, un nuevo test y modificaremos el nombre del anterior, de manera tal que 260 Programador PHP Experto Eugenia Bahit nuestro Test Case se vea como sigue: <?php require_once('contabilidad/models/BalanceContable.php'); class BalanceContableTest extends PHPUnit_Framework_TestCase { public function test_calcular_iva_con_1500_esperando_315() { $this->coverage = new BalanceContable(); $this->coverage->importe_bruto = 1500; $this->coverage->alicuota_iva = 21; $result = $this->coverage->calcular_iva(); $this->assertEquals(315, $result); } public function test_calcular_iva_con_2800_esperando_588() { $this->coverage = new BalanceContable(); $this->coverage->importe_bruto = 2800; $this->coverage->alicuota_iva = 21; $result = $this->coverage->calcular_iva(); $this->assertEquals(588, $result); } } ?> Ahora nuestro segundo test, debe fallar: eugenia@cocochito:~/borrador/MyApp$ phpunit Tests PHPUnit 3.4.5 by Sebastian Bergmann. .F Time: 0 seconds, Memory: 4.00Mb There was 1 failure: 1) BalanceContableTest::test_calcular_iva_con_2800_esperando_588 Failed asserting that <integer:315> matches expected <integer:588>. /home/eugenia/borrador/MyApp/Tests/BalanceContableTest.php:19 FAILURES! Tests: 2, Assertions: 2, Failures: 1. CUARTO PASO: Escribir el algoritmo necesario para hacer pasar el test Es hora de programar lo que nuestro m�todo, realmente necesita y sin hardcodear el retorno de resultados. Modificaremos nuestro m�todo calcular_iva() de la clase 261 Programador PHP Experto Eugenia Bahit BalanceContable a fin de escribir el algoritmo que efectivamente se encargue de calcular el valor de retorno: <?php class BalanceContable { public $importe_bruto; public $alicuota_iva; public function calcular_iva() { $iva = $this->alicuota_iva / 100; $neto = $this->importe_bruto * $iva; return $neto; } } ?> Nuestros test, ahora pasar�n: eugenia@cocochito:~/borrador/MyApp$ phpunit Tests PHPUnit 3.4.5 by Sebastian Bergmann. .. Time: 0 seconds, Memory: 4.00Mb OK (2 tests, 2 assertions) Es v�lido hacer notar, que a medida que se van escribiendo los test, �stos, no solo guiar�n nuestro desarrollo en cuanto a �lo nuevo que se debe escribir�, sino que adem�s, nos ir�n obligando a refactorizar el c�digo constantemente, en principio, mostr�ndonos el camino para eliminar redundancias y crear un dise�o m�s simple: <?php require_once('contabilidad/models/BalanceContable.php'); class BalanceContableTest extends PHPUnit_Framework_TestCase { public function test_calcular_iva_con_1500_esperando_315() { $this->coverage = new BalanceContable(); 262 Programador PHP Experto Eugenia Bahit $this->coverage->importe_bruto = 1500; $this->coverage->alicuota_iva = 21; $result = $this->coverage->calcular_iva(); $this->assertEquals(315, $result); } public function test_calcular_iva_con_2800_esperando_588() { $this->coverage = new BalanceContable(); $this->coverage->importe_bruto = 2800; $this->coverage->alicuota_iva = 21; $result = $this->coverage->calcular_iva(); $this->assertEquals(588, $result); } } ?> La redundancia anterior, nos est� diciendo que existen elementos de preparaci�n (arrange) comunes para nuestra Test Case: <?php require_once('contabilidad/models/BalanceContable.php'); class BalanceContableTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->coverage = new BalanceContable(); $this->coverage->alicuota_iva = 21; } public function test_calcular_iva_con_1500_esperando_315() { $this->coverage->importe_bruto = 1500; $result = $this->coverage->calcular_iva(); $this->assertEquals(315, $result); } public function test_calcular_iva_con_2800_esperando_588() { $this->coverage->importe_bruto = 2800; $result = $this->coverage->calcular_iva(); $this->assertEquals(588, $result); } } ?> RECUERDA: cada cambio que se haga al c�digo tanto del test como del SUT (c�digo de la aplicaci�n cubierta por test), 263 Programador PHP Experto Eugenia Bahit requiere que se vuelva a correr el test. 264 Programador PHP Experto Eugenia Bahit Unit Testing con PHPUnit Existen varios frameworks xUnit para Unit Testing en PHP, pero sin dudas, el �nico que ha demostrado contar con una gran cobertura de c�digo, estabilidad y buena documentaci�n, es PHPUnit. El manual oficial de PHPUnit (en ingl�s) puede encontrarse en: http://www.phpunit.de/manual/3.6/en/. Se puede instalar PHPUnit en sistemas operativos GNU/Linux, ejecutando los siguientes comandos: sudo pear upgrade PEAR pear config-set auto_discover 1 pear install pear.phpunit.de/PHPUnit Aunque en distribuciones basadas en Debian, puede hacerse directamente mediante la instalaci�n del paquete phpunit con apt-get: sudo apt-get install phpunit M�todos Assert de PHPUnit PHPUnit provee una gran cantidad de m�todos assert cuyas referencias, podemos encontrar en el Cap�tulo 4 del manual oficial: http://www.phpunit.de/manual/3.6/en/writing-tests-forphpunit. html 265 Programador PHP Experto Eugenia Bahit Algunas caracter�sticas comunes de los m�todos assert, son: � Generalmente, por cada m�todo assert existe su opuesto: assertContains() y assertNotContains(). � A la vez, cada m�todo assert deber� recibir m�nimamente un par�metro que ser� el resultado de ejecutar el c�digo del SUT. � Adicionalmente, a cada m�todo assert, se le puede pasar como par�metro opcional, un mensaje personalizado para ser arrojado en caso de error (generalmente, ser� el �ltimo par�metro). � Los m�todos assert que requieren el paso de dos par�metros obligatorios (valores que deben compararse entre s�), generalmente guardan el siguiente orden: metodoAssert($valor_esperado, $valor_recibido) Es decir, que en esos casos, siempre el primer par�metro ser� el valor esperado y el segundo par�metro, el valor recibido por la ejecuci�n del c�digo SUT. Veamos algunos ejemplos puntuales: <?php require_once('contabilidad/models/BalanceContable.php'); class BalanceContableTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->coverage = new BalanceContable(); $this->coverage->alicuota_iva = 21; } // AssertEquals($valor_esperado, $valor_recibido) public function test_calcular_iva() { $this->coverage->importe_bruto = 1500; $result = $this->coverage->calcular_iva(); $this->assertEquals(315, $result); } // AssertTrue($valor_recibido) 266 Programador PHP Experto Eugenia Bahit public function test_alcanzado_por_impuesto_de_importacion_con_160() { $this->coverage->importe_bruto = 160; $result = $this->coverage->alcanzado_por_impuesto_de_importacion(); $this->assertTrue($result); } // AssertNull($valor_recibido) public function test_alcanzado_por_impuesto_de_importacion_con_143() { $this->coverage->importe_bruto = 143; $result = $this->coverage->alcanzado_por_impuesto_de_importacion(); $this->assertNull($result); } } ?> C�digo fuente de la clase Test Case <?php class BalanceContable { public $importe_bruto; public $alicuota_iva; # Calcular IVA sobre un importe bruto public function calcular_iva() { $iva = $this->alicuota_iva / 100; $neto = $this->importe_bruto * $iva; return $neto; } # Determinar si un importe paga impuesto de importaci�n public function alcanzado_por_impuesto_de_importacion() { // importes mayores a 150 USD pagan impuesto if($this->importe_bruto > 150) { return True; } } } ?> C�digo fuente del SUT 267 Programador PHP Experto Eugenia Bahit Ejercicio Escribir el c�digo SUT del siguiente Test Case: <?php require_once('Usuario.php'); class UsuarioTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->coverage = new Usuario(); $this->coverage->username = "juanperez75"; $this->coverage->password = md5("pablito: clavo_1_palito"); } public function test_set_usuario_esperando_key() { $result = $this->coverage->set_usuario(); $this->assertArrayHasKey('Usuario', $result); } public function test_set_usuario_esperando_juanperez75() { $result = $this->coverage->set_usuario(); $this->assertEquals('juanperez75', $result['Usuario']); } public function test_set_usuario_esperando_pass() { $result = $this->coverage->set_usuario(); $this->assertEquals(md5('pablito: clavo_1_palito'), $result['Clave']); } } ?> 268 Programador PHP Experto Eugenia Bahit Unificaci�n del c�digo en Repositorios Un Repositorio es un espacio destinado a almacenar informaci�n digital. En nuestro caso, lo que se almacenar� en ese repositorio, ser�n los archivos -c�digo fuente, tarballs, binarios, etc- de nuestra aplicaci�n. Sus principales caracter�sticas son: � Espacio de almacenamiento centralizado de, principalmente, el c�digo fuente de la aplicaci�n as� como scripts de construcci�n -en el caso de aplicaciones que requieran ser compiladas o simplemente, necesiten realizar configuraciones especiales, ya sea tanto para continuar desarroll�ndolas como para ejecutarlas-. � Para ser efectivos, deben llevar un control hist�rico de cambios que se vayan efectuando en los archivos -preferentemente autom�tico-, permitir el establecimiento de tags -etiquetas- que ayuden a identificar diferentes releases -versiones-. Y todas esta caracter�stica, son aquellas que nos brindan los programas de control de versiones6. Sobre los Sistemas de Control de Versiones 6 http://es.wikipedia.org/wiki/Programas_p ara_control_de_versiones 269 Programador PHP Experto Eugenia Bahit Los Sistemas de Control de Versiones (SCV) pueden agruparse en dos tipos: � Centralizados: un �nico repositorio centralizado administrado por un solo responsable. � Distribuidos (recomendados): donde existe un repositorio central que cada usuario podr� clonar para obtener su propio repositorio -local- e interactuar con con otros repositorios locales. Entre los SCV distribuidos podemos destacar excelentes alternativas GPL (Software Libre), como es el caso de -entre otros-, Git (de Linus Torvalds, creador del Kernel Linux en el que se basa el Sistema Operativo GNU/Linux), Mercurial (desarrollado en Python y C) o el magn�fico Bazaar, nacido a partir de GNUArch y desarrollado �ntegramente en Python por Martin Pool, con el patrocinio de Canonical y elegido en este curso. Una gran ventaja de los SCV es que permiten a varios programadores trabajar simult�neamente sobre los mismos archivos, impidiendo que el trabajo de uno, pise al trabajo de otro. Los SCV pueden utilizarse tanto a trav�s de l�nea de comandos, como de aplicaciones gr�ficas. En este curso, nos centraremos en el uso por medio de l�nea de comandos. Los SCV, en su mayor�a -y a rasgos generales- cuentan con un conjunto de funcionalidades, las cuales, para cada una, existe 270 Programador PHP Experto Eugenia Bahit un determinado comando (generalmente, similar en la mayor�a de los SCV). Integraci�n continua con Bazaar Bazaar, cuenta con una estructura que puede dividirse en: 1. Repositorio: conjunto de revisiones 2. �rbol de trabajo: un directorio que contiene las revisiones con sus correspondientes ramas 3. Ramas: un conjunto ordenado de las diferentes revisiones con sus archivos correspondientes 4. Revisiones: es una vista espont�nea del estado de cada uno de los archivos, en un momento determinado Un repositorio, entonces, contiene un �rbol de trabajo, quien a la vez, puede estar integrado por varias ramas, las cuales almacenar�n las distintas versiones (revisiones) por las que ha ido transitando el Software. Instalaci�n de Bazaar N�tese que Bazaar deber� instalarse en cada una de las m�quinas donde se desee usar. Esto es, en los ordenadores que contar�n con repositorios locales y en el ordenador destinado a actuar como repositorio central. Para instalar Bazaar, por favor, visita 271 Programador PHP Experto Eugenia Bahit http://wiki.bazaar.canonical.com/Download para obtener las instrucciones necesarias. Bazaar por l�nea de comandos M�s adelante veremos todo lo que puede hacerse con Bazaar. Aqu�, nos limitaremos a conocer la sintaxis b�sica. bzr [-h|-v|-q] comando-interno-de-bazaar [argumentos] Las opciones globales (opcionales) -h, -v y -q, significan ayuda (sobre el comando interno), modo verboso (despliega mayor informaci�n sobre lo que sucede mientras se ejecuta el comando en cuesti�n) y silenciar (solo desplegar� errores y advertencias), respectivamente. Veremos los comandos internos de bazaar, en el siguiente t�tulo. No obstante, citar� algunos comandos �tiles a la propia aplicaci�n, que pueden servirnos para entender mejor de que se trata: bzr help commands Despliega una lista completa de los comandos internos y su descripci�n. bzr version Despliega informaci�n sobre la versi�n de Bazaar. bzr help comando-interno Despliega informaci�n de ayuda sobre el comando indicado. 272 Programador PHP Experto Eugenia Bahit Presentarse ante Bazaar Cada vez que enviemos cambios al repositorio, Bazaar tendr� que identificarnos a fin de poder ofrecer la informaci�n correcta sobre quien ha efectuado alguna revisi�n. Para ello, el primer paso, es presentarnos: bzr whoami "Juan Perez <juanperez@dominio.ext>" Iniciar un nuevo proyecto El siguiente paso, ser� crear un nuevo proyecto. Para ello, haremos lo siguiente: Primero inicializamos el repositorio central en el ordenador destinado a tal fin: __eugenia_1978_esAR__@mydream:/srv/repos$ bzr init-repo app-curso-xp Shared repository with trees (format: 2a) Location: shared repository: app-curso-xp Luego, all� mismo, creamos nuestro branch (al que llamaremos trunk): __eugenia_1978_esAR__@mydream:/srv/repos$ bzr init app-curso-xp/trunk Created a repository tree (format: 2a) Using shared repository: /srv/repos/app-curso-xp/ Clonar el repositorio central: crear los repositorios locales Es hora de que cada miembro del equipo, se traiga el branch desde el repositorio central. Esta actividad deber� realizarse en cada una de las m�quinas de cada uno de los miembros del equipo (previamente, deber�n presentarse ante Bazaar mediante bzr whoami). 273 Programador PHP Experto Eugenia Bahit Para clonar el repositorio, cada uno de los miembros del equipo, har� los siguiente: eugenia@cocochito:~/example$ bzr branch bzr+ssh://user@x.x.x.x/srv/repos/app-curso-xp/trunk user@x.x.x.x's password: Branched 0 revision(s). N�tese que la ruta hacia el branch debe formarse por: protocolo://usuario@host/ruta/al/branch/central N�tese que en el caso del protocolo SSH, debe anteponerse el prefijo bzr+ Si listamos el directorio donde clonamos el repo, podremos ver que ya tenemos nuestro branch local: eugenia@cocochito:~/example$ ls -lha total 12K drwxr-xr-x 3 eugenia eugenia 4,0K 2012-04-21 19:59 . drwxr-xr-x 65 eugenia eugenia 4,0K 2012-04-21 20:04 .. drwxr-xr-x 3 eugenia eugenia 4,0K 2012-04-21 19:59 trunk Podremos comprobar tambi�n, que efectivamente estamos �conectando� nuestro repo local con el central: eugenia@cocochito:~/example$ cd trunk/ eugenia@cocochito:~/example/trunk$ bzr info Standalone tree (format: 2a) Location: branch root: . Related branches: parent branch: bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/appcurso- xp/trunk/ 274 Programador PHP Experto Eugenia Bahit Nociones b�sicas para integrar c�digo de forma continua Crearemos ahora nuestro primer archivo (de prueba): eugenia@cocochito:~/example/trunk$ echo "Hola Mundo" > prueba.txt Y verificaremos cual es el estado actual de nuestro repo: eugenia@cocochito:~/example/trunk$ bzr st unknown: prueba.txt puede utilizarse tambien, bzr status Nos indica que hay un archivo desconocido. �Es el archivo que acabamos de crear! No te preocupes. El siguiente paso tras crear nuevos archivos y/o directorios, es avisarle a Bazaar, para lo cual, hay que agregar el archivo o directorio: eugenia@cocochito:~/example/trunk$ bzr add prueba.txt adding prueba.txt Volvemos a comprobar el estado: eugenia@cocochito:~/example/trunk$ bzr st added: prueba.txt Bazaar no est� informando que se ha realizado un cambio en el repositorio. Y todo cambio, debe ser informado Bazaar con un mensaje que lo describa: eugenia@cocochito:~/example/trunk$ bzr ci -m "Agregado archivo de prueba" Committing to: /home/eugenia/example/trunk/ added prueba.txt Committed revision 1. �Ya tenemos la primera revisi�n!!! Pero el commit, fue realizado 275 Programador PHP Experto Eugenia Bahit localmente. Nuestro repo central, a�n no se ha enterado y tenemos que enviar los cambios al repositorio central: eugenia@cocochito:~/example/trunk$ bzr push bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/app-curso-xp/trunk/ __eugenia_1978_esAR__@66.228.52.93's password: This transport does not update the working tree of: bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/app-curso-xp/trunk/. See 'bzr help working-trees' for more information. Pushed up to revision 1. Pero �por qu� nos dice que el �rbol de trabajo no se ha actualizado? Debemos actualizarlo en el repositorio central: __eugenia_1978_esAR__@mydream:/srv/repos/app-curso-xp/trunk$ bzr st working tree is out of date, run 'bzr update' __eugenia_1978_esAR__@mydream:/srv/repos/app-curso-xp/trunk$ bzr update +N prueba.txt All changes applied successfully. Updated to revision 1 of branch /srv/repos/app-curso-xp/trunk Diariamente, todos los miembros del equipo, deber�n traer los cambios desde el repo central: eugenia@cocochito:~/example/trunk$ bzr pull bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/app-curso-xp/trunk/ __eugenia_1978_esAR__@66.228.52.93's password: No revisions to pull. Guardando el path del repo central Puede ser extremadamente molesto, tener que estar indicando la direcci�n del repo central con cada pull y push que hagamos. Podemos evitar esto, editando el archivo de configuraci�n de nuestro repo local: eugenia@cocochito:~/example/trunk$ vim .bzr/branch/branch.conf y configuramos las variables pull_location y push_location (modificar la ruta por la que corresponda): 276 Programador PHP Experto Eugenia Bahit push_location = bzr+ssh://user@host/srv/repos/app-curso-xp/trunk/ pull_location = bzr+ssh://user@host/srv/repos/app-curso-xp/trunk/ Guardamos los cambios y ya podremos hacer pull y push, solo con el comando respectivo: eugenia@cocochito:~/example/trunk$ bzr pull Using saved parent location: bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/app-curso-xp/trunk/ __eugenia_1978_esAR__@66.228.52.93's password: No revisions to pull. eugenia@cocochito:~/example/trunk$ bzr push Using saved push location: bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/app-curso-xp/trunk/ __eugenia_1978_esAR__@66.228.52.93's password: This transport does not update the working tree of: bzr+ssh://__eugenia_1978_esAR__@66.228.52.93/srv/repos/app-curso-xp/trunk/. See 'bzr help working-trees' for more information. No new revisions to push. Integraci�n continua avanzada con Bazaar A la hora de trabajar con un SCV, aparece una larga lista de cuestiones, que ameritan especial cuidado. Por ejemplo �qu� sucede si por accidente, elimino un archivo en mi repositorio local? �C�mo lo recupero? O �qu� sucede si modifiqu� un archivo en mi repo local, sobre una versi�n distinta a la que est� en el repo central? Y estas, son solo dos de las decenas de preguntas que nos pueden surgir. Veremos aqu�, como solucionar cada una de las problem�ticas m�s frecuentes que se presentan en la vida diaria de la integraci�n continua. 277 Programador PHP Experto Eugenia Bahit Problema Descripci�n soluci�n Ignorar archivo Se desea evitar que un archivo o directorio sea enviado al repo central bzr ignore archivo bzr ignore archivo.txt Recuperar archivo Recuperar una versi�n anterior de un archivo o directorio bzr revert nro_revision bzr revert 2 Dejar de lado un archivo temporalmente Se desea evitar temporalmente, enviar los cambios de un archivo, al repo central bzr shelve archivo bzr shelve archivo.txt Recuperar cambios Se desea recuperar los cambios de un archivo, previamente salvado mediante shelve bzr unshelve archivo bzr unshelve archivo.txt Encontradas versiones diferentes de un mismo archivo Al traer los cambios desde el repo central, las modificaciones hechas sobre un archivo, interfieren con las efectuadas localmente al mismo archivo bzr merge -i archivo la opci�n -i es opcional y permite seleccionar los cambios de forma interactiva Merge no resolvi� el conflicto Tras combinar diferentes versiones, Bazaar informa de conflictos existentes Bzr conflict-diff archivo (generalmente requerir� solucionar el conflicto de forma manual. Conflictdiff mostrar� las diferencias encontradas que no pudieron resolverse con el merge) N�tese que toda vez que se indica la palabra �archivo�, se hace referencia no solo a archivos sino tambi�n a directorios. Resumen de comandos de uso frecuente Comando Descripci�n add Agregar archivo o directorio check Verifica la consistencia del �rbol de trabajo ci Env�a mensajes al repo informando cambios (debe ejecutarse siempre despu�s de realizar cambios y antes de enviar cambios al repo central con push) conflictdiff Muestra las diferencias que generan conflictos en un archivo deleted Muestra la lista de archivos eliminados ignore Ignora un archivo ignored Muestra la lista de archivos ignorados 278 Programador PHP Experto Eugenia Bahit info Muestra informaci�n sobre el �rbol de trabajo log Muestra los cambios hist�ricos (revisiones) de un branch merge Combina los cambios centrales con los locales en un archivo (tras el merge, siempre se debe hacer commit � generalmente: bzr ci -m �merge� ) mv Mueve o renombra un archivo o directorio pull Enviar cambios al repo central push Traer cambios desde el repo central remerge Elimina un merge anterior remove Elimina archivos o directorios renames Muestra la lista de archivos que han sido renombrados resolve Marca resuleto un conflicto revert Recupera un archivo a una revisi�n anterior revno Muestra el n�mero de la revisi�n actual shelve Deja temporalmente a un lado, un determinado archivo que no se desee enviar a�n al repo central st Muestra el estado del repo (localmente) � cuando no muestra nada, significa que todo est� en orden y se pueden enviar cambios al repo central sin conflictos. Cualquier otro mensaje, requerir� intervenir para resolverlo, antes de enviar cualquier cambio al repo central. tag Taguea (etiqueta) una revisi�n (la remueve o modifica seg�n las opciones pasadas como argumentos) tags Retorna la lista completa de tags uncommit Revierte un commit efectuado anteriormente unshelve Recupera los cambios de un archivo salvado mediante shelve update Actualiza el �rbol de trabajo trayendo el �ltimo commit enviado whoami Presentarse ante Bazaar (u obtener los datos de presentaci�n aportados con anterioridad) 279 Programador PHP Experto Eugenia Bahit Resumen para uso diario de Bazaar Esta, es una breve s�ntesis, de los pasos diarios a seguir, cuando se trabaja con repositorios. Al comienzo, podr�a ser muy �til, imprimir esta planilla e ir marcando los casilleros vac�os a medida que se completa cada paso. �Cu�ndo? �Qu�? �C�mo? Al comenzar el d�a Actualizar el repo local bzr pull Mergear archivos con diferencias (si aplica) bzr merge bzr ci -m "merge" Resolver conflictos en archivos, no resueltos con un merge: nombre_archivo (archivo conflictivo mergeado localmente) nombre_archivo.BASE (original del repo central) nombre_archivo.OTHER (archivo con las modificaciones hechas por otro miembro del equipo) nombre_archivo.THIS (archivo modificado por uno mismo, localmente) visualizar los archivos en conflicto y corregirlos. Eliminar el conflicto bzr resolve Luego de Trabajar libremente sobre tus archivos 280 Programador PHP Experto Eugenia Bahit actualizar el repo local Al concluir un task (tarea) Verificar el estado de los cambios bzr st Agregar archivos nuevos (reportados como unknow) bzr add nombre_archivo Eliminar archivos obsoletos bzr remove nombre_archivo Ignorar archivos que no deban actualizarse en el repo central bzr ignore nombre_archivo Comitear los cambios bzr ci -m "breve descripci�n de la tarea terminada" Correr todos los test y verificar que pasen php -f test python -m unittest discover -v Enviar cambios al repo central bzr push Al finalizar el d�a Actualizar el repo CENTRAL bzr update Los pasos que se encuentran sombreados en tono m�s claro, son opcionales, y depender�n de cada caso en particular. Ver el historial de revisiones: bzr log | Obtener el nro. de revisi�n actual: bzr revno 281 Programador PHP Experto Eugenia Bahit Refactoring Veremos aqu�, la quinta pr�ctica sugerida por eXtreme Programming, con mayores detalles y algunos ejemplos. Refactoring, como comentamos anteriormente, es una t�cnica que consiste en mejorar el c�digo fuente de una aplicaci�n (limpiarlo), sin que dichas modificaciones, afecten el comportamiento externo del sistema. Existen diferentes tipos de refactorizaciones que pueden ser necesarias implementar al c�digo de nuestra aplicaci�n. Cada tipo, representa una t�nica diferente de refactorizaci�n. Por ejemplo, eliminar c�digo redundante, requiere de una t�cnica diferente a dividir los algoritmos de un m�todo para crear m�todos derivados. Sin embargo, hablar de t�cnicas de refactorizaci�n puede resultar confuso, ya que la refactorizaci�n en s� misma es una t�cnica, que ofrece diferentes soluciones a cada tipo de problema. Por lo tanto, es preferible pensar la refactorizaci�n como una �nica t�cnica que propone diferentes soluciones a cada tipo de problema. El problema En principio, habr�a que diferenciar el t�rmino �problema� de la palabra �error�, para no generar confusiones. El error en s�, es una falla en el c�digo fuente, que impide el correcto comportamiento del sistema. Mientras que el problema, puede 282 Programador PHP Experto Eugenia Bahit definirse como �algo que huele mal en el c�digo fuente�7 pero sin embargo, no impide el correcto funcionamiento de la aplicaci�n. Los problemas que se pueden presentar en el c�digo fuente de una aplicaci�n, dependen de much�simos factores, que en gran parte de los casos, encuentran una relaci�n directa con el paradigma de programaci�n empleado as� como en el lenguaje que se utilice. Si se intentara abarcar todos los problemas posibles, la lista podr�a tornarse infinita, tediosa y hasta inutil o muy confusa. Es por ello, que solo abarcaremos los problemas m�s frecuentes, que puedan considerarse generales, independientes al lenguaje pero m�s cercanos al paradigma de la programaci�n orientada a objetos. La soluci�n Indefectiblemente, la soluci�n a cada problema ser� la refactorizaci�n y auqnue resulte redundante, la soluci�n, depender� de cada problema. Sin embargo, como regla general, la soluci�n deber� comenzar por identificar el momento en el cual llevarla a cabo. 7 Kent Beck, uno de los creadores de eXtreme Programming, es quien introdujo el t�rmino �bad smells� (malos olores) para referirse de manera global, a aquellas expresiones y algoritmos poco claros que generan confusi�n en el c�digo fuente de un sistema, torn�ndolo m�s complejo de lo que deber�a ser. 283 Programador PHP Experto Eugenia Bahit Cu�ndo y c�mo tomar la desici�n de refactorizar Tres strikes y �Refactoriza! En el mundo de la refactorizaci�n, haciendo una anolog�a con el b�isbol, suele utilizarse la regla �Tres Strike8 y �refactoriza!�. Esto puede descrbirse an�logamente como: �la primera vez que hagas algo, solo hazlo. La segunda vez que hagas algo similar, notar�s que est�s duplicando c�digo, pero lo har�s de todas formas. La tercera vez que te enfrentes al mismo caso, refactoriza�. Cuando se est� programando una aplicaci�n con TDD, como hemos visto anteriormente, el proceso de desarrollo se est� dividiendo en dos acciones concretas: programar y refactorizar. Esto es, a medida que vamos creando nuevos m�todos, vamos refactorizando el c�digo para eliminar redundancias y en definitiva, hacer el c�digo -del test- m�s legible y as� obtener un mejor rendimiento. Pero no estamos refactorizando el SUT constantemente, puesto que �ste, tiene un momento y lugar para ser refactorizado. La refactorizaci�n del SUT, implica que lo primero que debemos hacer, es cumplir el objetivo (programar aquello que se necesita) y luego refactorizar el c�digo del SUT, cada vez 8 En el b�isbol, un strike es una anotaci�n negativa para el bateador ofensivo, cuando la pelota no es lanzada hacia el diamante. Al tercer strike anotado, termina el turno del bateador. 284 Programador PHP Experto Eugenia Bahit que: � Se agregue un nuevo m�todo � Se corrija un bug � Se haga una revisi�n de c�digo Pero siempre, respetando la regla de �los tres strikes�. Una vez identificado el momento, solo ser� cuesti�n de identificar el problema a fin de poder elegir la soluci�n indicada. Una soluci�n a cada problema Como comentamos anteriormente, no haremos una extensa lista de problemas, sino que nos centraremos en problemas generales. Muchas de las soluciones sugeridas en este cap�tulo, han sido extra�das de SourceMaking.com9, sitio donde se puede encontrar una completa clasificaci�n de problemas10 y sus respectivas soluciones11. Como hemos hecho a lo largo del curso, iremos de lo general a lo particular y de lo particular al detalle. Variables de uso temporal mal implementadas En principio, definiremos a las variables de uso temporal, como aquellas variables que son asignadas en el �mbito local de un m�todo de clase y son necesarias temporalmente, solo en ese m�todo, sin ser llamadas o requeridas por otros m�todos. 9 http://sourcemaking.com/refactoring . N�tese que algunas de las t�cnicas expuestas en el sitio Web referido, no se mencionan en este curso, por considerarlas poco apropiadas. Esto es debido a que algunas pr�cticas son m�s espec�ficas de lenguajes como Java, mientras que a otras, las considero contrarias a las buenas pr�cticas de la programaci�n orientada a objetos y por lo tanto, contraproducentes. 10 �Bad Smells in Code� http://sourcemaking.com/refactoring/bad-smells-in-code 11 Diferentes t�cnicas de refactorizaci�n: http://sourcemaking.com/refactoring 285 Programador PHP Experto Eugenia Bahit Generalmente representan un problema en los siguientes casos: 1) Variables de uso temporal que definen una acci�n concreta: $var = ($a * $b ) / (int)$c; En el ejemplo anterior, vemos una variable de uso temporal, que define una acci�n concreta: dividir el producto de dos factores. Esto representa un problema, ya que las acciones son responsabilidad de los m�todos y no de las variables. En estos casos, la soluci�n, es transferir la responsabilidad de la acci�n a un m�todo: $var = dividir_producto($a, $b, $c); function dividir_producto($a, $b, $c) { return ($a * $b ) / (int)$c; } N�tese que variables de uso temporal que definen un valor directo: $var = 15; o por el retorno de la llamada a una funci�n: $var = strlen($variable); no necesitan transferir su responsabilidad a otro m�todo. 2) Variables de uso temporal son requeridas por m�s de un m�todo: function metodo_a() { $a = 15; $b = 100; $c = 2; $var = self::dividir_producto($a, $b, $c); // continuar... 286 Programador PHP Experto Eugenia Bahit } private static function dividir_producto($a, $b, $c) { return ($a * $b ) / $c; } En el ejemplo, anterior, las variables temporales $a, $b y $c, son requeridas por dos m�todos y se est�n definiendo como tales en un m�todo, requiriendo ser pasadas como par�metros. Aqu�, la soluci�n, ser� convertir las variables temporales, en propiedades de clase: function metodo_a() { self::$a = 15; self::$b = 100; self::$c = 2; $var = self::dividir_producto(); // continuar... } private static function dividir_producto() { return (self::$a * self::$b ) / self::$c; } 3) Variables de uso temporal que reasignan par�metros: function foo($a) { $a = strtoupper($a); // continuar ... } En casos como �ste, la confusi�n puede ser grande: un par�metro es un par�metro y una variable temporal, una variable temporal. Es entonces, cuando variables temporales no deben tener el mismo nombre que los par�metros: function foo($a) { $b = strtoupper($a); // continuar ... } 287 Programador PHP Experto Eugenia Bahit M�todos que reciben par�metros Aqu� debe hacerse una notable distinci�n entre par�metros, variables de uso temporal y propiedades de clase. Y esta distinci�n, est� dada por la finalidad de cada una: � Las variables de uso temporal, como hemos visto antes, est�n destinadas a definir un valor concreto al cual se har� referencia solo en el �mbito donde se haya definido. � Las propiedades de clase, son caracter�sticas inherentes al objeto a las cuales se har� referencia desde diversos �mbitos. � Y finalmente, los par�metros, ser�n valores adicionales, que no pueden ser considerados propiedades del objeto pero que sin embargo, son requeridos para que una acci�n, modifique las propiedades de un objeto. Class Usuario { function validar_usuario($username, $pass) { if($username == 'pepe' && $pass == '123') { return True; } } } En el ejemplo anterior, claramente los par�metros $username y $pass, deber�an ser propiedades del objeto Usuario puesto que son caracter�sticas intr�nsecas al objeto. Como regla general, los par�metros deben ser evitados toda vez que sea posible, reemplaz�ndolos por propiedades de clase: Class Usuario { 288 Programador PHP Experto Eugenia Bahit function validar_usuario() { if($this->username == 'pepe' && $this->pass == '123') { return True; } } } Expresiones extensas Muchas veces, podremos encontrarnos con expresiones que debido a su extensi�n, se hacen dif�ciles de leer y cuando no, confusas: return ((in_array('abc', $array) || in_array('bcd', $array)) && (in_array('cde', $array) || in_array('def', $array))) ? 'OK' : 'ERROR'; Cuando estamos en presencia de expresiones tan extensas, lo mejor es -aqu� s�- utilizar variables de uso temporal para simplificar dichas expresiones: $a = in_array('abc', $array); $b = in_array('bcd', $array); $c = in_array('cde', $array); $d = in_array('def', $array); $ab = ($a || $b); $cd = ($c || $d); return ($ab && $cd) ? 'OK' : 'ERROR'; M�todos extensos No solo una expresi�n puede ser extensa. Muchas veces, nos encontraremos con m�todos con extensos algoritmos que realizan varias acciones: function renderizar_plantilla($data=array(), $pattern, $template) { $ini_pattern = "[[INI-PATTERN-{$pattern}]]"; $end_pattern = "[[END-PATTERN-{$pattern}]]"; $plantilla = file_get_contents($template); 289 Programador PHP Experto Eugenia Bahit $pos_ini = strpos($plantilla, $ini_pattern); $pos_fin = strpos($plantilla, $end_pattern); $longitud_cadena = $pos_fin - $pos_ini; $cadena = substr($plantilla, $pos_ini, $longitud_cadena); $reemplazos = ''; foreach($data as $identificador=>$valor) { $reemplazos .= str_replace("[{$identificador}]", $valor, $cadena); } $resultado = str_replace($cadena, '[[NUEVO-CONTENIDO]]', $plantilla); $resultado = str_replace('[[NUEVO-CONTENIDO]]', $reemplazos, $plantilla); return $resultado; } Cuando existen m�todos tan extensos, probablemente, la soluci�n consista en la combinaci�n de diversas t�cnicas, que van desde agrupar expresiones en una misma l�nea hasta evitar la asignaci�n de variables temporales (como vimos al comienzo) y extraer c�digo llev�ndolo a diferentes m�todos: function renderizar_plantilla($data=array()) { self::set_patterns(); self::set_contenido_plantilla(); self::get_pattern(); self::reemplazar_datos($data); return str_replace('[[NEW]]', self::$reemplazos, self::set_new_pattern()); } // extracci�n de c�digo para crear nuevo m�todo // y sustituci�n de par�metros por propiedades de clase static function set_patterns() { self::$ini_pattern = "[[INI-PATTERN-{self::$pattern}]]"; self::$end_pattern = "[[END-PATTERN-{self::$pattern}]]"; } // extracci�n de c�digo para crear nuevo m�todo // y sustituci�n de par�metros por propiedades de clase static function set_contenido_plantilla() { self::$contenido = file_get_contents(self::$template); } // extracci�n de c�digo para crear nuevo m�todo // sustituci�n de par�metros por propiedades de clase // y sustituci�n de expresiones en l�nea static function get_pattern() { self::$cadena = substr(self::$contenido, strpos(self::$ini_pattern), (self::$pos_fin � self::$pos_ini)); } // extracci�n de c�digo para crear nuevo m�todo 290 Programador PHP Experto Eugenia Bahit // y sustituci�n de par�metros por propiedades de clase static function reemplazar_datos($data=array()) { self::$reemplazos = ''; foreach($data as $identificador=>$valor) { self::$reemplazos .= str_replace("[{$identificador}]", $valor, self:: $cadena); } } // extracci�n de c�digo para crear nuevo m�todo static function set_new_pattern() { return str_replace(self::$cadena, '[[NEW]]', self::$contenido); } C�digo duplicado en una misma clase Es frecuente -y de lo m�s com�n-, que las mismas expresiones, comiencen a duplicarse en diferentes m�todos de una misma clase: function metodo_1() { $a = strip_tags(self::$propiedad); $a = htmlentities(self::propiedad); return self::metodo_a() . self::$propiedad; } function metodo_2() { $a = strip_tags(self::$propiedad); $a = htmlentities(self::propiedad); return self::$propiedad . self::metodo_b() . self::metodo_c(); } Las expresiones duplicadas en el c�digo de los diferentes m�todos de una misma clase, se solucionan extrayendo el c�digo duplicado de los m�todos, y coloc�ndolo en un nuevo m�todo de clase: function metodo_1() { self::metodo_3(); return self::metodo_a() . self::$propiedad; } function metodo_2() { self::metodo_3(); return self::$propiedad . self::metodo_b() . self::metodo_c(); } 291 Programador PHP Experto Eugenia Bahit static function metodo_3() { self::$propiedad = strip_tags(self::$propiedad); self::$propiedad = htmlentities(self::propiedad); } C�digo duplicado en varias clases con la misma herencia El caso anterior puede darse tambi�n, cuando el c�digo se encuentra duplicado en diferentes m�todos de clases con la misma herencia: class B extends A { function metodo_1() { $a = strip_tags(self::$propiedad); $a = htmlentities(self::propiedad); return self::metodo_a() . self::$propiedad; } } class C extends A { function metodo_2() { $a = strip_tags(self::$propiedad); $a = htmlentities(self::propiedad); return self::$propiedad . self::metodo_b() . self::metodo_c(); } } En estos casos, en los cu�les existen dos o m�s clases que heredan de la misma clase, se extrae el c�digo duplicado en los m�todos de las clases hijas, y con �ste, se crea un nuevo m�todo de en la clase madre: class A { static function metodo_3() { self::$propiedad = strip_tags(self::$propiedad); self::$propiedad = htmlentities(self::propiedad); } } 292 Programador PHP Experto Eugenia Bahit class B extends A { function metodo_1() { self::metodo_3(); return self::metodo_a() . self::$propiedad; } } class C extends A { function metodo_2() { self::metodo_3(); return self::$propiedad . self::metodo_b() . self::metodo_c(); } } C�digo duplicado en varias clases sin la misma herencia Como era de esperarse, el c�digo tambi�n podr� aparecer duplicado en diferentes clases pero que no tienen la misma herencia: class B { function metodo_1() { $a = strip_tags(self::$propiedad); $a = htmlentities(self::propiedad); return self::metodo_a() . self::$propiedad; } } class C { function metodo_2() { $a = strip_tags(self::$propiedad); $a = htmlentities(self::propiedad); return self::$propiedad . self::metodo_b() . self::metodo_c(); } } En estos casos, la soluci�n es extraer el c�digo duplicado, crear una nueva clase y con el c�digo extra�do, crear un 293 Programador PHP Experto Eugenia Bahit m�todo para esta nueva clase que podr� ser heredada por las anteriores o simplemente, instanciada: class A { static function metodo_3($parametro) { return htmlentities(strip_tags($parametro)); } } class B { function metodo_1() { return self::metodo_a() . A::metodo_3(self::$propiedad); } } class C { function metodo_2() { return A::metodo_3(self::$propiedad) . self::metodo_b() . self::metodo_c(); } } 294 Programador PHP Experto Eugenia Bahit Introducci�n a la Arquitectura de Software �Qu� es la arquitectura de software? Es necesario aclarar, que no existe una definici�n �nica, exacta, abarcadora e inequ�voca de �arquitectura de software�. La bibliograf�a sobre el tema es tan extensa como la cantidad de definiciones que en ella se puede encontrar. Por lo tanto tratar�, no de definir la arquitectura de software, sino m�s bien, de introducir a un concepto simple y sencillo que permita comprender el punto de vista desde el cual, este libro abarca a la arquitectura de software pero, sin �nimo de que ello represente �una definici�n m�s�. A grandes rasgos, puede decirse que �la Arquitectura de Software es la forma en la que se organizan los componentes de un sistema, interact�an y se relacionan entre s� y con el contexto, aplicando normas y principios de dise�o y calidad, que fortalezcan y fomenten la usabilidad a la vez que dejan preparado el sistema, para su propia evoluci�n�. Atributos de calidad La Calidad del Software puede definirse como los atributos impl�citamente requeridos en un sistema que deben ser satisfechos. Cuando estos atributos son satisfechos, puede decirse (aunque en forma objetable), que la calidad del software es satisfactoria. Estos atributos, se gestan desde la 295 Programador PHP Experto Eugenia Bahit arquitectura de software que se emplea, ya sea cumpliendo con aquellos requeridos durante la ejecuci�n del software, como con aquellos que forman parte del proceso de desarrollo de �ste. Atributos de calidad que pueden observarse durante la ejecuci�n del software 1. Disponibilidad de uso 2. Confidencialidad, puesto que se debe evitar el acceso no autorizado al sistema 3. Cumplimiento de la Funcionalidad requerida 4. Desempe�o del sistema con respecto a factores tales como la capacidad de respuesta 5. Confiabilidad dada por la constancia operativa y permanente del sistema 6. Seguridad externa evitando la p�rdida de informaci�n debido a errores del sistema 7. Seguridad interna siendo capaz de impedir ataques, usos no autorizados, etc. Atributos de calidad inherentes al proceso de desarrollo del software 1. Capacidad de Configurabilidad que el sistema otorga al usuario a fin de realizar ciertos cambios 2. Integrabilidad de los m�dulos independientes del sistema 3. Integridad de la informaci�n asociada 4. Capacidad de Interoperar con otros sistemas (interoperabilidad) 5. Capacidad de permitir ser modificable a futuro (modificabilidad) 296 Programador PHP Experto Eugenia Bahit 6. Ser f�cilmente Mantenible (mantenibilidad) 7. Capacidad de Portabilidad, es decir que pueda ser ejecutado en diversos ambientes tanto de software como de hardware 8. Tener una estructura que facilite la Reusabilidad de la misma en futuros sistemas 9. Mantener un dise�o arquitect�nico Escalable que permita su ampliaci�n (escalabilidad) 10. Facilidad de ser Sometido a Pruebas que aseguren que el sistema falla cuando es lo que se espera (testeabilidad) Niveles de abstracci�n Podemos decir que la AS se compone de tres niveles de abstracci�n bien diferenciados: Estilo Arquitect�nico, Patr�n Arquitect�nico y Patr�n de Dise�o. Existe una diferencia radical entre estos tres elementos, que debe marcarse a fin de evitar las grandes confusiones que inevitablemente, concluyen en el mal entendimiento y en los resultados poco satisfactorios. �stos, son los que en definitiva, aportar�n �calidad� al sistema resultante. En lo sucesivo, trataremos de establecer la diferencia entre estos tres conceptos, viendo como los mismos, se relacionan entre s�, formando parte de un todo: la arquitectura de software. Estilo Arquitect�nico, Patr�n Arquitect�nico y Patr�n de Dise�o, representan -de lo general a lo particular- los tres niveles de abstracci�n que componen la Arquitectura de Software. 297 Programador PHP Experto Eugenia Bahit Estilo Arquitect�nico El estilo arquitect�nico define a niveles generales, la estructura de un sistema y c�mo �ste, va a comportarse. Mary Shaw y David Garlan, en su libro �Software Architecture� (Prentice Hall, 1996), definen los estilos arquitect�nicos como la forma de determinar el los componentes y conectores de un sistema, que pueden ser utilizados a instancias del estilo elegido, conjuntamente con un grupo de restricciones sobre como �stos pueden ser combinados: 298 ESTILO ARQUITECT�NICO PATR�N ARQUITECT�NICO PATR�N DE DISE�O Niveles de abstracci�n en la Arquitectura de Software Generalidades Particularidades Detalles Programador PHP Experto Eugenia Bahit �[...] an architectural style determines the vocabulary of components and connectors that can be used in instances of that style, together with a set of constraints on how they can be combined [...]� Mary Shaw y David Garlan -en el mismo libro-, hacen una distinci�n de estilos arquitect�nicos comunes, citando como tales a: 1. Pipes and filters (filtros y tuber�as) 2. Data Abstraction and Object-Oriented Organization (Abstracci�n de datos y organizaci�n orientada a objetos) 3. Event-based (estilo basado en eventos) 4. Layered Systems (Sistemas en capas) 5. Repositories (Repositorios) 6. Table Driven Interpreters Viendo la clasificaci�n anterior, es muy frecuente que se encuentren relaciones entre los estilos arquitect�nicos y los paradigmas de programaci�n. Sin embargo, debe evitarse relacionarlos en forma directa. Patr�n Arquitect�nico Un patr�n arquitect�nico, definir� entonces, una plantilla para construir el Software, siendo una particularidad del estilo arquitect�nico elegido. 299 Programador PHP Experto Eugenia Bahit En esta definici�n, es donde se incluye a MVC, patr�n que a la vez, puede ser enmarcado dentro del estilo arquitect�nico orientado a objetos (estilo arquitect�nico basado en el paradigma de programaci�n orientada a objetos). Patr�n de Dise�o Dentro de niveles de abstracci�n de la arquitectura de Software, los patrones de dise�o representan el nivel de abstracci�n m�s detallado. A nivel general, nos encontramos con el Estilo Arquitect�nico. En lo particular, hallamos al Patr�n Arquitect�nico y, finalmente, el Patr�n de Dise�o es �el detalle�. Matt Zandstra en su libro �PHP Objects, Patterns and Practice� (Apress, 2010) define los patrones de dise�o como: �[�] is a problem analyzed with good practice for its solution explained [...]� (un problema analizado con buenas pr�cticas para su soluci�n explicada) Un patr�n de dise�o, entonces, es un an�lisis mucho m�s detallado, preciso y minucioso de una parte m�s peque�a del sistema, que puede incluso, trabajar en interacci�n con otros patrones de dise�o. Por ejemplo, un Singleton puede coexistir con un Factory y �stos, a la vez, con un Abstract Factory. En este sentido, un Patr�n Arquitect�nico como MVC, podr�a utilizar diversos patrones de dise�o en perfecta coexistencia, para la creaci�n de sus componentes. 300 Programador PHP Experto Eugenia Bahit Introducci�n al Patr�n Arquitect�nico MVC MVC -por sus siglas en ingl�s, model-view-controller (modelovista- controlador)- es un patr�n arquitect�nico que nos permite desarrollar aplicaciones, manteniendo separada la l�gica de negocios de las vistas, utilizando un �controlador� como conector (o intermediario) entre ambas. Entendiendo el funcionamiento de MVC En MVC, todo comienza con una petici�n del usuario. En una aplicaci�n Web, la petici�n del usuario podr�a ser, por ejemplo, �agregar un nuevo registro�. � �C�mo realiza esta petici�n el usuario? A trav�s del navegador. � �C�mo se identifica la petici�n? Por medio de la URL ingresada por el usuario. Un ejemplo: Tenemos una aplicaci�n Web, cuyos m�dulos son: M�dulo de Usuarios 301 Programador PHP Experto Eugenia Bahit M�dulo de Proyectos Cada m�dulo, a la vez, se encontrar� dividido en �modelos� (objetos): M�dulo de Usuarios ? Modelo Usuario ? Modelo Permiso Y estos modelos, ofrecer�n diversos recursos (funcionalidades de cada modelo): M�dulo de Usuarios ? Modelo Usuario ? Recursos: ? Agregar Usuario ? Modificar Usuario ? Eliminar Usuario ? Obtener Usuario Las peticiones del usuario, entonces, se realizar�n v�a navegador, siendo descifradas por la URL ingresada, la cual, guardar� un formato sugerido como el siguiente: dominio/modulo/modelo/recurso[/atributos] 302 Programador PHP Experto Eugenia Bahit Esto significa, que si el usuario desea agregar un nuevo usuario, su petici�n, deber�a ser: http://app.dominio.com/usuarios/usuario/agregarusuario/ � �A qui�n efect�a la petici�n el usuario? Al controlador. � �C�mo maneja el controlador, la petici�n del usuario? A trav�s de Handler. Un handler (o �manejador�) de peticiones, es un objeto encargado de gestionar las peticiones del usuario a nivel de la aplicaci�n. Este handler, descifrar� dichas peticiones, realizando un trabajo de pseudo ingenier�a inversa, sobre la URI. Por ejemplo, para la URI anterior, el handler podr�a realizar los siguientes pasos: 1) Identificar el dominio, eliminarlo del contexto y as� solo obtener el �ltimo tramo de la URI: $dominio = "http://{$_SERVER['SERVER_NAME']}"; $uri = "{$dominio}{$_SERVER['REQUEST_URI']}"; $ultimo_tramo = str_replace("{$dominio}/", NULL, $uri); 2) Hacer un explode del �ltimo tramo de la URI, para obtener 303 Programador PHP Experto Eugenia Bahit un array de 3 elementos (m�dulo, modelo y recurso respectivamente): $partes = explode("/", $ultimo_tramo); 3) Finalmente, el objeto handler, retornar� al controlador, el m�dulo, el modelo y el recurso solicitados por el usuarios, y ser� el controlador, quien prosiga con el resto. � �Qu� hace el controlador una vez que recibe la informaci�n retornada por el handler? La analiza para saber a que modelo deber� instanciar. Una vez que el controlador recibe la informaci�n retornada por el handler, procede a analizarla en conjunto. El handler me envi� la siguiente informaci�n: M�dulo: usuarios 304 Realiza petici�n CONTROLADOR handler invoca retorna Descifra petici�n Programador PHP Experto Eugenia Bahit Modelo: usuario Recurso: agregar-usuario Entonces, debo agregar un nuevo usuario. Para ello, voy a instanciar el objeto, modificar sus propiedades y llamar al m�todo correspondiente. � �Qu� hace el controlador con la informaci�n retornada por el modelo? La entrega a la vista. � �Para qu� el controlador le entrega la informaci�n a la vista? Para que �sta, se la muestre al usuario. 305 Instancia al objeto Modifica sus propiedades Llama al m�todo correspondiente CONTROLADOR handler MODELO Retorna Programador PHP Experto Eugenia Bahit 306 CONTROLADOR handler MODELO VISTA Env�a informaci�n retornada por el modelo Muestra la informaci�n retornada por el controlador Programador PHP Experto Eugenia Bahit Modelos en MVC Un modelo, en MVC, es una clase. Como patr�n que gu�a nuestras arquitecturas, MVC nos obliga a escribir clases �puras� que respeten el verdadero esp�ritu de la orientaci�n a objetos. Objetos puros: caracter�sticas de un modelo 1. Una clase, debe representar solo y �nicamente, un �modelo� para crear un objeto �nico. 2. Las propiedades de un objeto, siempre deber�n ser -solo y �nicamente- �sustantivos� cuyo valor, pueda ser definido por una cualidad o m�s de una. 3. Cuando una propiedad, sea definida por m�s de una cualidad, deber� considerarse la opci�n de componer el objeto: dicha propiedad, adquirir� las cualidades de otro objeto. 4. Las propiedades no compuestas del objeto -mayormente-, deben poder modificarse de forma directa, sin necesidad de requerir la llamada a un m�todo. 5. Las propiedades compuestas -mayormente-, deber�n contener un m�todo -en la clase- del tipo set_propiedad(Objeto $objeto) para ser modificadas. 6. Los m�todos de un objeto, deber�n ser �acciones� intr�nsecas del objeto y jam�s, podr�n representar acciones gen�ricas, que puedan ser consideradas ajenas al objeto. Por ejemplo, un objeto jam�s podr�a contener entre sus m�todos, una acci�n destinada a filtrar datos para prevenir inyecciones SQL. 307 Programador PHP Experto Eugenia Bahit 7. Solo los modelos, podr�n interactuar con la base de datos. No se podr� acceder a una base de datos, desde un �mbito externo a los modelos. 8. Preferentemente, cada modelo acceder� a la base de datos, mediante otro objeto- conector (capa de abstracci�n que pertenecer� al n�cleo de la aplicaci�n). Creando modelos bajo MVC en PHP Herencia y Composici�n Piensa en una camisa como un objeto. �Qu� propiedades podemos encontrar? Una camisa tendr� un tipo de tela determinado con un color espec�fico, botones, un talle, etc. �cierto? Podr�amos decir que la clase para crear una camisa, podr�a tener las siguientes propiedades: class Camisa { public $tela = "seda"; public $botones = 8; public $color = "blanca"; public $talle = 42; } Imag�nate que trabajas en el sal�n de ventas de una camiser�a y un cliente te pregunta �De qu� material son los botones de la camisa? �Son botones ciegos o con ojales? �qu� color son? Podr�amos deducir, que cada uno de los 8 botones, tiene caracter�sticas particulares: boton.material = "nylon" 308 Programador PHP Experto Eugenia Bahit boton.color = "marfil" boton.ojales = 2 Cuando hablamos de una propiedad con m�ltiples cualidades, como Arquitectos, debemos pensar -en principio- en dos opciones: herencia o composici�n. Un bot�n �no es acaso un objeto con atributos propios? Es all�, donde probablemente, comiencen a surgir dudas. La primera pregunta que deber�s hacerte, entonces, es: �Qu� relaci�n existe entre Camisa y Bot�n? Para responder a esta pregunta, deber�s hacerlo formulando nuevas inc�gnitas: � �Es Objeto A una extensi�n ampliada de Objeto B? Si la respuesta es s�, pensar�s en herencia. Si la respuesta es no, te har�s la siguiente pregunta: � Objeto B �forma parte integrante de Objeto A? Si la respuesta en s�, pensar�s entonces en composici�n. Entonces, la pregunta es: �Es Camisa una extensi�n ampliada de Bot�n? La respuesta clara es �No�. Pues Camisa y Bot�n, nada tienen en com�n. Sin embargo, Bot�n, forma parte integrante de Camisa: # archivo: models/camisa.php class Camisa { public $tela = "seda"; public $botones = array(); public $color = "blanca"; public $talle = 42; public function set_botones(Boton $boton, $cuantos_botones) { $iterar = 0; while($iterar < $cuantos_botones) { 309 Programador PHP Experto Eugenia Bahit $this->botones[] = $boton; $iterar++; } } } # archivo: models/boton.php class Boton { public $material = "nylon"; public $color = "marfil"; public $ojales = 2; } # archivo: crear_camisa.php $camisa = new Camisa(); $camisa->set_botones(new Boton(), 8); $botones = count($camisa->botones); if($camisa->botones[0]->ojales < 1) { $tipo_boton = "ciego"; } else { $tipo_boton = "ojalado"; } print "La camisa de {$camisa->tela} color {$camisa->color} tiene {$botones} botones de {$camisa->botones[0]->material} {$tipo_boton}s en color {$camisa->botones[0]->color}"); /* Salida: La camisa de seda color blanca tiene 8 botones de nylon ojalados en color marfil */ Acceso a bases de datos Un �objeto-conector� a nivel del core, que act�e como intermediario entre los modelos y la base de datos, ser� muy recomendable para respetar el verdadero esp�ritu del patr�n arquitect�nico MVC. Este objeto, ser� una clase preferentemente est�tica. Es decir, que necesitar� ser instanciada para hacer uso de los m�todos destinados a acceder a las bases de datos. 310 Programador PHP Experto Eugenia Bahit C�digo fuente de una capa de abstracci�n a nivel del core class DBObject { protected static $conn; protected static $stmt; protected static $reflection; protected static $sql; protected static $data; public static $results; protected static function conectar() { self::$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); self::$conn->autocommit(False); } protected static function preparar() { self::$stmt = self::$conn->prepare(self::$sql); self::$reflection = new ReflectionClass('mysqli_stmt'); } protected static function set_params() { $method = self::$reflection->getMethod('bind_param'); $method->invokeArgs(self::$stmt, self::$data); } protected static function get_data($fields) { $method = self::$reflection->getMethod('bind_result'); $method->invokeArgs(self::$stmt, $fields); while(self::$stmt->fetch()) { self::$results[] = unserialize(serialize($fields)); } } protected static function finalizar() { self::$stmt->close(); self::$conn->close(); } public static function ejecutar($sql, $data, $fields=False) { self::$sql = $sql; self::$data = $data; self::conectar(); self::preparar(); self::set_params(); self::$stmt->execute(); if($fields) { self::get_data($fields); } else { self::$conn->commit(); } self::finalizar(); } 311 Programador PHP Experto Eugenia Bahit } Implementaci�n desde el modelo: require_once('/app/core/db_object.php'); class Boton { public $material = NULL; public $color = NULL; public $ojales = NULL; public $boton_id = 0; public function save() { $sql = "INSERT INTO boton (material, color, ojales) VALUES (?, ?, ?)"; $data = array("ssi", "{$this->material}", "{$this->color}", "{$this->ojales}"); return DBObject::ejecutar($sql, $data); } public function get() { $sql = "SELECT material, color, ojales FROM boton WHERE boton_id > ?"; $data = array("i", "{$this->boton_id}"); $fields = array("Material"=>"", "Color"=>"", "Ojales"=>""); DBObject::ejecutar($sql, $data, $fields); return DBObject::$results; } } Object Relational Mapping (ORM) Si observamos la clase Boton, notaremos que el m�todo save(), declara �l mismo la sentencia SQL que ser� utilizada por DBObject para agregar registros en la base de datos. Pero una capa de abstracci�n a bases de datos, podr�a -y deber�aotorgar una abstracci�n mucho m�s completa. 312 Programador PHP Experto Eugenia Bahit El Mapeo Relacional de Objetos (Object Relational Mapping) es una t�cnica de programaci�n que nos permite vincular los objetos (modelos) de nuestra aplicaci�n, con una base de datos relacional, otorg�ndonos de manera colateral, una abstracci�n completa entre los modelos y el motor de la base de datos. Ventajas y desventajas del utilizar ORMs Sin dudas, las principales ventajas del ORM -en orden de prioridades-, son: 1. Independizar los modelos de las bases de datos, lo cual nos obliga a razonar nuestra aplicaci�n, 100% orientada a objetos (ya no pensaremos en como crear las tablas, solo crearemos nuestros modelos -modelar la app-). 2. Independencia del motor de base de datos: este punto es fundamental, ya que un buen ORM debe ser capaz de mapear los modelos de forma tal que las llamadas a sus m�todos, sean capaces de conectarse a cualquier tipo de base de datos y general consultas con el lenguaje SQL de la misma. Esta caracter�stica, es la que permite una mayor portabilidad de la aplicaci�n. 3. Acelera el proceso de desarrollo, puesto que se ahorra tiempo tanto en la creaci�n y dise�o de las bases de datos como en la escritura de las consultas. Sin embargo, la utilizaci�n de ORM, trae aparejadas ciertas desventajas que no pueden ser obviadas al momento de tomar una decisi�n: � Lenguaje de consulta propio: la mayor�a de las 313 Programador PHP Experto Eugenia Bahit librer�as ORM disponibles en el mercado, poseen su propio lenguaje de consulta a bases de datos. Esta caracter�stica, que err�neamente es considerada una ventaja por muchos autores (debido al escaso conocimiento del lenguaje SQL) genera una pseudoabstracci�n (o abstracci�n falaz) que induce a errores en el modelado de objetos, puesto que estos pseudolenguajes que ofrecen los ORM, no son m�s que una especie de �lenguaje SQL resumido�. � Reduce el rendimiento de la aplicaci�n, puesto que el proceso de conversi�n desde el pseudo-lenguaje al lenguaje SQL de la base de datos y el de �sta a objetos, demanda un mayor consumo de recursos. � Baja escalabilidad: tambi�n derivado del uso de pseudo-lenguajes de consulta propio, cuanto m�s robusta y compleja se va haciendo una aplicaci�n, mayor necesidad tendr� el programador, de escribir consultas en lenguaje SQL �crudo�. ORM Frameworks para PHP En PHP, existen prestigiosos frameworks ORM, que podr�amos utilizar en nuestra aplicaciones. Entre los m�s destacados, podremos encontrar: � Doctrine: http://www.doctrine-project.org/ � Propel: http://www.propelorm.org/ � Entre otros. Bibliograf�a recomendada Para complementar la lectura sobre modelos en PHP, se recomienda leer bibliograf�a sobre: 314 Programador PHP Experto Eugenia Bahit � Programaci�n orientada a objetos � Patrones de dise�o Libros recomendados: � PHP Objects, Patterns and Practice (Third Edition) � Matt Zandstra (Apress, 2010) � Pro PHP � Patterns, Frameworks, Testing and More (Part I: OOP and Patterns) � Kevin McArthur (Apress, 2008) � Pro PHP Programming (Chapter I:Object Orientation) � Peter Maclntyre, Brian Danchilla & Mladen Gogala (Apress, 2011) 315 Programador PHP Experto Eugenia Bahit Las vistas Embeber c�digo HTML, CSS y JavaScript en el c�digo PHP, con MVC queda en el olvido. Sin embargo, muchos Frameworks MVC para PHP, a�n conservan esa mala costumbre que los programadores, solemos arrastrar de la programaci�n estructurada. Basaremos este curso, en el esp�ritu original y m�s desarrollado de MVC, el cual nos propone una completa abstracci�n de las vistas, subdividiendo �stas, en la parte l�gica (c�digo PHP que se encargar� de hacer un render de la parte gr�fica) y en la GUI (parte gr�fica inherente al dise�o gr�fico), incorporando algunos Katas para no volver a embeber c�digo y lograr mantener un verdadero dise�o orientado a objetos. �Por d�nde empezar a desarrollar las vistas? Lo primero que debemos tener en cuenta, es la completa abstracci�n de la l�gica de su correspondiente GUI (Grafical User Interface). El dise�o gr�fico es un verdadero arte, que abarca m�ltiples disciplinas y que, como programadores, el peor error que podemos cometer, es querer convertir al dise�o gr�fico en un �producto cient�fico�, ya que la l�gica utilizada en el arte, debe indefectiblemente estar sometida al ingenio y creatividad, puesto que de lo contrario, se desaprovechar�an todas las virtudes que el arte, tiene para ofrecernos a fin de hacer nuestro Software �m�s humano�. 316 Programador PHP Experto Eugenia Bahit La GUI de nuestras aplicaciones, debe -s� y solo s�basarse en cuestiones inherentes al arte y la creatividad, al servicio de la usabilidad y experiencia del usuario. El primer paso, entonces, consistir� en crear las interfaces gr�ficas de la aplicaci�n. Es sumamente importante, considerar la posibilidad de contar con artistas expertos en el Dise�o Gr�fico y experiencia del usuario, puesto que son los �nicos que cuentan con la capacidad necesaria y la autoridad profesional suficiente, para crear verdaderas GUI. En este curso, no haremos demasiado �nfasis -ni ahondaremosen temas relativos al dise�o gr�fico, puesto que el curso, est� basado en la AS. Los ejemplos que utilizaremos, lejos est�n de ser tomados como par�metro de arte aplicado al dise�o gr�fico. Nos enfocaremos entonces, solo y �nicamente, en aquellos aspectos que como programadores y/o arquitectos, debemos considerar transmitir a los profesionales encargados de dise�ar la GUI de nuestras aplicaciones. Desarrollando la GUI Componentes de la GUI La GUI deber� estar compuesta, por todos aquellos archivos -y datos- est�ticos, que son ejecutados del lado del cliente. Entre ellos, nos encontramos con archivos HTML, CSS, JS, im�genes, archivos de audio y video y cualquier otro tipo de documento est�tico (como PDFs, ODTs, etc...). 317 Programador PHP Experto Eugenia Bahit Arquitectura Refiri�ndonos a Arquitectura como �Arquitectura de Software� (AS) y no, como Arquitectura de la Informaci�n (AI), la GUI de nuestra aplicaci�n, deber� estar concentrada en un solo lugar. Esto puede ser: � Un directorio exclusivo dentro de la App myapp/ +-- site_media / � Un servidor independiente del servidor de la aplicaci�n La estructura m�s recomendada que puede tener la GUI, es aquella que siga una organizaci�n como la que sigue: myapp/ +-- site_media / +-- css/ � +-- core/ Archivos CSS aplicables a toda la app � +-- module-a/ Archivos CSS pertenecientes solo al m�dulo A � +-- module-b/ Archivos CSS pertenecientes solo al m�dulo B +-- html/ � +-- core / � +-- module-a / � +-- module-b / +-- img/ � +-- core / 318 Programador PHP Experto Eugenia Bahit � +-- module-a / � +-- module-b / +-- js/ +-- core / +-- module-a / +-- module-b/ Esta estructura de directorios, si bien es la m�s escalable de todas, l�gicamente podr� variar de acuerdo a las necesidades de cada app. Por ejemplo, tambi�n podr� incluir otros directorios destinados a almacenar archivos de audio, de video, PDFs, etc. Preparando la GUI para interactuar con la l�gica Los archivos de la GUI (m�s espec�ficamente, aquellos destinados como maqueta de nuestra app -generalmente, archivos HTML), solo podr�n contener lenguaje de marcado| dise�o exclusivamente. Pero �Qu� sucede con aquellos datos que deber�n sustituirse din�micamente? <!doctype html> <head> <meta charset="utf-8"> <title>Listado de Camisas</title> </head> <body> <header> <h1>MyApp: Camisas</h1> <nav> <ul> <li><a href="menu-option-1" title="Menu Option 1">Ver camisas</a></li> <li><a href="menu-option-2" title="Menu Option 2">Agregar camisa nueva</a></li> </ul> </nav> 319 Programador PHP Experto Eugenia Bahit </header> <section> <header> <h2>Camisas creadas</h2> </header> <table> <tr> <th>ID</th> <th>Camisa</th> <th>Tela</th> </tr> <tr> <td>1</td> <td>Tombolini Home</td> <td>Seda italiana blanca</td> </tr> </table> </section> </body> </html> Si miramos el c�digo anterior, observando el texto en negritas, podremos notar que de tratarse de un template, esos datos, deber�n ser plasmados din�micamente. Es en estos casos, donde el dise�adores, deber� utilizar �comodines� que luego, la l�gica de esa vista, se encargar� de renderizar. Comodines �Qu� son y c�mo implementarlos? Un comod�n, puede ser cualquier texto plano que nos ayude a identificar -como programadores, en nuestra l�gica de negocios- aquellos datos que necesitan ser sustituidos din�micamente. Estos comodines, deber�n seguir un patr�n que, por un lado, permita al dise�ador tener una vista previa real de su dise�o y por otro, permita a nuestra l�gica de negocios, identificarlos con facilidad. Algunos ejemplos: [TEXTO IDENTIFICADOR] 320 Programador PHP Experto Eugenia Bahit {TEXTO IDENTIFICADOR} Lo importante, es que en toda la GUI, siempre se utilice el mismo patr�n: <!doctype html> <head> <meta charset="utf-8"> <title>{TITULO DE PAGINA}</title> </head> <body> <header> <h1>MyApp: {MODULO}</h1> <nav> {MENU} </nav> </header> <section> <header> <h2>{SUBTITULOS}</h2> </header> {TABLA} </section> </body> </html> {TABLA} y {MENU} ser�n a la vez, dos nuevos archivos HTML. En el segundo caso (men�), ser� sencillo: <ul> <li><a href="ver-camisas">Ver camisas</a></li> <li><a href="crear-camisa">Crear camisa</a></li> </ul> Pero en el caso de la tabla �c�mo haremos para obtener identificadores iterativos? Si miramos el c�digo de la tabla que listar� las camisas, podremos notar que la fila destinada a la descripci�n de cada registro, deber� repetirse tantas veces como registros se encuentren: 321 Programador PHP Experto Eugenia Bahit <table> <tr> <th>ID</th> <th>Camisa</th> <th>Tela</th> </tr> <!-- a partir de aqu� los datos deben iterar --> <tr> <td>{CAMISA ID}</td> <td>{CAMISA DESCRIPCION}</td> <td>{CAMISA DATOS TELA}</td> </tr> <!-- desde aqu�, ya no iteran --> </table> De la misma forma que en el c�digo anterior, un comentario explica que esa fila debe ser iterada, ser� como los dise�adores, deber�n especific�rnoslo a nosotros, solo que de forma m�s simple y siguiendo alg�n patr�n, como por ejemplo: Para identificar d�nde inicia una iteraci�n: <!-- iniciar-loop: NOMBRE DE IDENTIFICADOR DEL LOOP --> Para identificar d�nde finaliza: <!-- finalizar-loop: NOMBRE DE IDENTIFICADOR DEL LOOP --> Por ejemplo, el dise�ador podr�a hacer lo siguiente: <!-- iniciar-loop: CAMISA --> <tr> <td>{CAMISA ID}</td> <td>{CAMISA DESCRIPCION}</td> <td>{CAMISA DATOS TELA}</td> </tr> <!-- finalizar-loop: CAMISA --> 322 Programador PHP Experto Eugenia Bahit Dise�ando la l�gica de negocios La l�gica de negocios de las vistas, es aquella que se encargar� de traer el contenido est�tico y sustituir los �comodines� din�micamente, con los datos que le sean entregados por el controlador. La l�gica de negocios, tendr� un dise�o distribuido: � Una parte -reutilizable y com�n a toda la aplicaci�n- a nivel del core � Otra parte, a nivel del m�dulo, mediante la cual, cada modelo tendr� su propia l�gica myapp/ +-- core � +-- view.php +-- module � +-- models � � +-- boton.php � � +-- camisa.php � +-- views � +-- boton.php � +-- camisa.php +-- site_media La l�gica principal, ser� la del core. �sta, consistir� en una librer�a que nos provea de los m�todos necesarios para: � Traer los templates 323 Programador PHP Experto Eugenia Bahit � Identificar comodines e iteraciones � Sustituir los comodines din�micamente � Retornar el render del template La l�gica de cada modelo, ser� una extensi�n de la l�gica principal, con m�todos propios que requieran de los m�todos heredados de la clase principal, para que cada render, sea espec�fico y particular. L�gica principal Como comentamos anteriormente, la l�gica principal ser� una librer�a -una clase- con m�todos que se encarguen, a niveles generales, de traer templates y renderizarlos. Un ejemplo de ello, ser�a la siguiente clase (a nivel del core), llamada RenderTemplate: # archivo /myapp/core/render_template.php class RenderTemplate { public $file = ""; public $data = array(); protected $comodines = array(); protected $values = array(); public $html = ""; # Traer contenido HTML de una plantilla protected function get_html($str=NULL) { return isset($str) ? $str : file_get_contents($this->file); } # Setear comodines y valores public function set_data($data=array()) { $this->comodines = array_keys($data); $this->values = array_values($data); $this->set_comodines(); } # Modificar comodines (envolver entre llaves) private function set_comodines() { foreach($this->comodines as &$comodin) { $comodin = "{" . $comodin . "}"; } 324 Programador PHP Experto Eugenia Bahit } # Renderizar plantilla public function render_template($str=NULL) { $this->html .= str_replace($this->comodines, $this->values, $this->get_html($str)); } } Esta clase, podr� ser heredada, por cualquier vista, pudiendo acceder y modificar cualquier propiedad o m�todo, p�blico o protegido. Veamos un ejemplo de implementaci�n en detalle: Tenemos la siguiente plantilla HTML: <!-- archivo: /myapp/site_media/html/template.html --> <!doctype html> <head> <meta charset="utf-8"> <title>{TITULO}</title> </head> <body> <header> <h1>MyApp: {MODULO}</h1> </header> <section> <header> <h2>{SUBTITULO}</h2> </header> {CONTENIDO} </section> </body> </html> De la plantilla anterior, necesitamos reemplazar 4 comodines: TITULO, MODULO, SUBTITULO y CONTENIDO. Hasta ahora, solo contamos con un modelo (Boton) y la l�gica de la vista a nievel del core, RenderTemplate. No tenemos ni un controlador, ni la l�gica de la vista para el modelo, as� que probaremos 325 Programador PHP Experto Eugenia Bahit implementar la clase RenderTemplate desde un archivo de pruebas ubicado en /myapp/module/prueba.php. Lo primero que necesitaremos, ser� importar la clase RenderTemplate: require_once("../core/render_template.php"); El paso siguiente, ser� definir un array asociativo, donde las claves sean los comodines y los valores, los datos por los cuales esos comodines, deber�n ser reemplazados: $data = array("TITULO" => "Administraci�n de Camisas", "MODULO" => "Camisas", "SUBTITULO" => "Listado de Botones", "CONTENIDO" => "Aqu� debo mostrar el listado de botones"); Finalmente, tendremos que: # Crear una instancia de RenderTemplate $view = new RenderTemplate(); # Preparar los datos a ser renderizados $view->set_data($data); El m�todo set_data de la clase RenderTemplate, se encargar� de: � Dividir el array asociativo en dos. Un array lo destinar� a los comodines (en la propiedad $comodines y el segundo, ser� almacenado en la propiedad $values � A los comodines, les agregar� las llaves de apertura y cierre, para generar el patr�n que los identifique en la plantilla como tales. 326 Programador PHP Experto Eugenia Bahit # Modificar la propiedad $file indicando el path del template HTML a ser renderizado $view->file = "../site_media/html/template.html"; # Llamar al m�todo render_template $view->render_template(); # Imprimir el render resultante print $view->html; RenderTemplate, almacena el resultado del render en la propiedad p�blica $html. Pero una cuesti�n muy importante es �qu� sucede si necesitamos hacer un render iterativamente? Seguramente, podr�amos utilizar esta misma clase, almacenando �mini-plantillas� con el c�digo HTML de las iteraciones, y luego, hacer un bucle en la llamada a set_data y render_template. Pero esto, ser�a poco escalable, redundante y hasta tedioso. Supongamos que CONTENIDO, a veces pueda ser un texto, otras un leyer con im�genes o una tabla (como vimos al principio) que deba completarse din�micamente, dependiendo de la cantidad de registros devueltos por una consulta a la DB. Colocar la tabla, texto o layer en el template.html, ser�a contraproducente. Pues ese contenido es din�mico. Pero a la vez, ese mismo contenido que podr� variar, tambi�n variar� de forma din�mica. Por ejemplo: <table> <tr> <th>Material</th> <th>Color</th> <th>Ojales</th> 327 Programador PHP Experto Eugenia Bahit </tr> <!-- aqu� comienza un loop con reemplazos din�micos --> <tr> <td>{Material}</td> <td>{Color}</td> <td>{Ojales}</td> </tr> <!-- aqu� finaliza el loop --> </table> Lo m�s conveniente entonces, ser� que cada uno de esos contenidos (tablas, layers, textos, etc), se almacenen en una nueva plantilla HTML, identificando cada fragmento a ser reemplazado iterativamente, como vimos al comienzo: <!-- archivo: /myapp/site_media/html/lista_botones.html --> <table> <tr> <th>Material</th> <th>Color</th> <th>Ojales</th> </tr> <!-- ini-loop: BOTONES --> <tr> <td>{Material}</td> <td>{Color}</td> <td>{Ojales}</td> </tr> <!-- end-loop: BOTONES --> </table> Hasta aqu�, logramos resolver el tema de las plantillas HTML (GUI de la vista). Para obtener el reemplazo del comod�n CONTENIDO, solo necesitar�amos hacer un file_get_contents de esta nueva plantilla: $data = array("TITULO" => "Administraci�n de Camisas", "MODULO" => "Camisas", "SUBTITULO" => "Listado de Botones", "CONTENIDO" => file_get_contents('../site_media/html/lista_botones.html'); Sin embargo �C�mo le decimos a RenderTemplate, que adem�s de reemplazar todos los comodines anteriores, 328 Programador PHP Experto Eugenia Bahit deber� reemplazar los del loop �BOTONES� de nuestra tabla? La soluci�n, ser� crear una nueva clase reutilizable (a nivel del core), para renderizar datos din�micamente, que a la vez, herede de RenderTemplate. Veamos como ser�a: # Archivo: /myapp/core/render_data.php class RenderData extends RenderTemplate { protected $pattern_tags = array(); protected $pattern = ""; # Setear pattern tags protected function set_tag($tag) { $this->pattern_tags = array("<!-- ini-loop: {$tag} -->", "<!-- end-loop: {$tag} -->"); } # Obtener posici�n de los pattern tags private function get_position($tag) { return strpos($this->get_html(), $this->pattern_tags[$tag]); } # Obtener longitud total del pattern private function get_longitud() { $longitud = $this->get_position(1) - $this->get_position(0); return $longitud + strlen($this->pattern_tags[1]); } # Setear el contenido del pattern protected function set_pattern_content() { $this->pattern = substr($this->get_html(), $this->get_position(0), $this->get_longitud()); } # Eliminar el patr�n HTML y sustituirlo por el render private function delete_pattern() { $str_final = str_replace($this->pattern_tags, "", $this->html); return str_replace($this->pattern, $str_final, $this->get_html()); } # Renderizar datos public function render_data($tag, $data) { $this->set_tag($tag); $this->set_pattern_content(); foreach($data as $array) { $this->set_data($array); $this->render_template($this->pattern); } 329 Programador PHP Experto Eugenia Bahit $this->html = $this->delete_pattern(); } } Lo primero que har� esta nueva clase, es setear el patr�n que identifica los tags de inicio y finalizaci�n del fragmento HTML a renderizar din�micamente: protected function set_tag($tag) { $this->pattern_tags = array("<!-- ini-loop: {$tag} -->", "<!-- end-loop: {$tag} -->"); } Este m�todo, necesitar� que se le indique el nombre del identificador (tag) que en nuestro ejemplo, ser� �BOTONES�. El siguiente paso que realiza, es obtener el contenido HTML de todo el fragmento. De eso se encarga el m�todo set_pattern_content. Veamos como act�a este m�todo: protected function set_pattern_content() { $this->pattern = substr($this->get_html(), # m�todo de la clase principal $this->get_position(0), $this->get_longitud()); } Este m�todo, se vale de la funci�n substr de PHP, para obtener un fragmento de c�digo de una cadena de texto. La cadena de texto que almacena el c�digo fuente HTML, ser� retornada por el m�todo get_html de la clase principal: protected function get_html($str=NULL) { return isset($str) ? $str : file_get_contents($this->file); } Si no se le pasa una string, retornar� el contenido HTML del 330 Programador PHP Experto Eugenia Bahit template seteado en la propiedad $file. La posici�n de inicio para extraer una �sub-cadena�, la obtiene a trav�s del m�todo get_position(): private function get_position($tag) { return strpos($this->get_html(), $this->pattern_tags[$tag]); } Este m�todo, recibe el �ndice del tag correspondiente (recordemos que se setean solo 2) y simplemente se vale de strpos para obtener la posici�n en la cual dicho tag se encuentra. Y finalmente, para conocer la longitud total de la cadena, llama al m�todo get_longitud(): private function get_longitud() { $longitud = $this->get_position(1) - $this->get_position(0); return $longitud + strlen($this->pattern_tags[1]); } Este m�todo, se basa simplemente en una l�gica matem�tica. La longitud total del patr�n, ser� igual a la diferencia entre la posici�n de inicio del tag de apertura del bucle, y de la del tag de cierre del bucle, m�s la longitud de ese tag. Veamos un peque�o ejemplo, que nos permita entender esto, haciendo una comprobaci�n manual: Soy <b>una frase</b> corta Necesitamos obtener �nicamente, el texto envuelto en negritas (pero tambi�n necesitamos que las etiquetas �b� nos sean retornadas. 331 Programador PHP Experto Eugenia Bahit Contemos los caracteres: S o y < b > u n a f r a s e < / b > c o r t a 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 �C�mo obtendremos <b>una frase</b>? Haremos lo siguiente: 1. Le diremos a strpos que nos retorne la posici�n de nuestro primer patr�n <b>. strpos nos retornar� 4. 2. Luego le pediremos la posici�n de nuestro segundo patr�n </b> y nos devolver� 16. 3. Para conocer la longitud entre 4 y 16, necesitamos una simple resta: 16 � 4 = 12. 4. Ahora, cuenta (en la tabla) desde el 4, 12 casilleros. �Llegamos al casillero 15! A�n nos resta obtener nuestro patr�n de cierre </b>. Para ello, simplemente le pedimos a strlen que nos devuelva la longitud de nuestro patr�n de cierre. Nos dir� 4. Entonces, solo sumamos esos 4 a 12: 12 + 4 = 16. 5. Si contamos 16 casilleros desde el 4, obtendremos nuestro patr�n completo (desde el 4 al 19 inclusive). No te dejes confundir por la igualdad de los n�meros: Da la casualidad que el inicio del primer patr�n se produce en la posici�n 4 y que la longitud del segundo patr�n tambi�n es 4. Esto es coincidencia. El primer patr�n podr�a iniciar en la posici�n 7 y la 332 Programador PHP Experto Eugenia Bahit longitud del segundo patr�n podr�a ser 19! S� o s�, el c�lculo debe ser el que hemos hecho. Tenemos nuestra l�gica a nivel del core, totalmente terminada. Contamos con dos clases de las cuales podremos heredar indistintamente. Pero si heredamos de RenderData, ser� como �matar dos p�jaros de un tiro�. Desde el archivo de prueba anterior, veremos como implementar entonces, ambas l�gicas, a trav�s de RenderData, pero esta vez, utilizaremos tambi�n nuestro modelo: require_once('../core/settings.php'); require_once('../core/db_object.php'); require_once("../core/render_template.php"); require_once("../core/render_data.php"); require_once('models/boton.php'); $boton = new Boton(); $boton->boton_id = 10; $data = $boton->get(); $view = new RenderData(); $view->file = "../site_media/html/lista_botones.html"; $view->render_data('BOTONES', $data); $contenido = $view->html; $data = array("TITULO" => "Administracion de Camisas", "MODULO" => "Camisas", "SUBTITULO" => "Listado de Botones", "CONTENIDO" => $contenido); $view = new RenderTemplate(); $view->set_data($data); $view->file = "../site_media/html/template.html"; $view->render_template(); print $view->html; 333 Programador PHP Experto Eugenia Bahit L�gica a nivel de m�dulo Lleg� la hora de desarrollar la l�gica de las vistas para nuestros modelos. Recordemos tres cosas: 1. La l�gica de la vista para cada modelo, ser� la encargada de mostrar (hacer un echo|print) el render del HTML al usuario; 2. Los datos que debe renderizar (datos din�micos), se los deber� entregar el controlador (la l�gica de la vista no puede conectarse con el modelo. Solo el controlador puede hacerlo). 3. Contamos con los m�todos necesarios para renderizar una plantilla HTML y datos iterativamente. Entonces, �por descarte�, nuestras vistas solo deber�n encargarse de: 1. Definir los comodines a ser reemplazados 2. Solo cuando sea necesario, asociar los comodines con los datos recibidos desde el controlador 3. Modificar las propiedades de la l�gica principal, definiendo los archivos a ser renderizados y haciendo la llamada a los m�todos de esa l�gica, que apliquen a cada caso 4. Finalmente, deber� mostrarle esos renders al usuario Caracter�sticas m�nimas que debe tener la l�gica Debe ser una clase que herede de la l�gica principal: class NombreDeLaVista extends RenderData { } Cada vista, debe estar asociada al modelo: 334 Programador PHP Experto Eugenia Bahit class BotonView extends RenderData { } M�nimamente, cada vista deber� tener un m�todo p�blico (que pueda ser llamado desde el controlador), para cada uno de los m�todos p�blicos del modelo al que pertenezca: class BotonView extends RenderData { public function save_view() { } public function get_view() { } } Deber� estar preparada para recibir datos desde el controlador. Ya sea, contener una propiedad p�blica que el controlador pueda modificar: class BotonView extends RenderData { public $data = NULL; public function save_view() { } public function get_view() { } } O sino, definir en cada m�todo, el|los par�metro|s necesario|s para recibir estos datos: class BotonView extends RenderData { public function save_view($mensaje='') { } public function get_view($registros=array()) { } } Los datos que la vista reciba desde el controlador, 335 Programador PHP Experto Eugenia Bahit generalmente ser�n de alguno de los siguientes tipos: � Mensajes retornados por el modelo al controlador � Datos retornados al controlador por el modelo Cuando no haya sido necesario que el controlador contactase con el modelo (por ejemplo, cuando la petici�n del usuario sea ver un formulario para agregar un nuevo registro), luego de hacer el switch, el controlador contactar� directamente con la vista. Para ello, la vista debe estar preparada, para recibir un llamado de petici�n. En estos casos, el tipo de datos recibido, ser� una petici�n que generalmente, estar� representada por la llamada a un m�todo: class BotonView extends RenderData { public function save_view($mensaje='') { } public function get_view($registros=array()) { } public function mostrar_formulario_alta_boton() { } } En otros casos, la vista podr� tener un m�todo p�blico, que simplemente switchee la petici�n y ella misma, haga la llamada a un m�todo de s� misma: class BotonView extends RenderData { public function save_view($mensaje='') { } public function get_view($registros=array()) { } public function recibir_peticion($peticion) { switch ($peticion) { case 'form-nuevo-boton': $this->mostrar_formulario_alta_boton(); break; } } 336 Programador PHP Experto Eugenia Bahit private function mostrar_formulario_alta_boton() { } } Creando la l�gica de la vista para un modelo A continuaci�n, bas�ndonos en las caracter�sticas que definimos en el punto anterior y, en nuestro archivo de pruebas, crearemos la l�gica de la vista encargada de mostrar el listado de botones. Esta clase, tendr� por el momento, solo tres m�todos: � Un m�todo constructor, encargado de setear el diccionario de datos inicial (array asociativo donde las claves ser�n los comodines) � Otro m�todo p�blico, para ser llamado por el controlador (ser� quien se encargue de hacer el render interativo) � Y un tercer m�todo privado para renderizar y mostrar el template general Nuestro c�digo, se ver� as�: # Archivo: /myapp/module/views/boton.php class BotonView extends RenderData { public function __construct() { $this->dict = array("TITULO" => "Administracion de Camisas", "MODULO" => "Camisas", "SUBTITULO" => "", "MENU" => "", "CONTENIDO" => ""); } public function get_boton($registros=array()) { $this->file = "../site_media/html/lista_botones.html"; $this->render_data('BOTONES', $registros); $this->dict['SUBTITULO'] = "Listado de Botones"; $this->dict['CONTENIDO'] = $this->html; $this->html = NULL; $this->mostrar(); } 337 Programador PHP Experto Eugenia Bahit private function mostrar() { $this->set_data($this->dict); $this->file = "../site_media/html/template.html"; $this->render_template(); print $this->html; } } Finalmente, modificaremos nuestro archivo de pruebas, para simular la implementaci�n de lo anterior: require_once('../core/settings.php'); require_once('../core/db_object.php'); require_once("../core/render_template.php"); require_once("../core/render_data.php"); require_once('models/boton.php'); require_once('views/boton.php'); $boton = new Boton(); $data = $boton->get(); $view = new BotonView(); $view->get_boton($data); El resultado final, ser� el template y la tabla del listado de botones, renderizada: <!doctype html> <head> <meta charset="utf-8"> <title>Administracion de Camisas</title> </head> <body> <header> <h1>MyApp: Camisas</h1> <nav> </nav> </header> <section> <header> <h2>Listado de Botones</h2> </header> <table> 338 Programador PHP Experto Eugenia Bahit <tr> <th>Material</th> <th>Color</th> <th>Ojales</th> </tr> <tr> <td>nylon</td> <td>verde</td> <td>4</td> </tr> <tr> <td>madera</td> <td>caoba</td> <td>0</td> </tr> <tr> <td>madera</td> <td>verde</td> <td>0</td> </tr> <tr> <td>metal</td> <td>oro</td> <td>0</td> </tr> </table> </section> </body> </html> 339 Programador PHP Experto Eugenia Bahit El controlador: el alma de MVC En MVC, el controlador es parte esencial de la arquitectura. El controlador es una especie de �intermediario� entre el modelo y la vista. Es quien los conecta. Podemos imaginar al controlador, como un t�nel conector entre el modelo y la vista: Cada m�dulo, contendr� entonces, un controlador para conectar cada uno de los modelos del m�dulo, con la vista correspondiente. myapp/ +-- core +-- module � +-- models � � +-- boton.php � � +-- camisa.php 340 MODELO controlador VISTA Programador PHP Experto Eugenia Bahit � +-- views � � +-- boton.php � � +-- camisa.php � +-- controllers � +-- boton.php � +-- camisa.php +-- site_media A la vez, el usuario llegar� a cada controlador, por media de un controlador general, a nivel del n�cleo de la aplicaci�n: Y este controlador general, manejar� las peticiones del usuario, a trav�s de un handler (Application Handler): 341 MODELO controlador VISTA FRONT CONTROLLER Programador PHP Experto Eugenia Bahit Front Controller: controlando la aplicaci�n a nivel del core El Front Controller ser� el encargado de manejar las peticiones del usuario a nivel de la aplicaci�n por medio de un Application Handler y hacer la llamada al controlador correspondiente, el que depender�, de cu�l haya sido la petici�n del usuario. Toda la aplicaci�n, entonces, se manejar� desde el Front Controller, significando este hecho, que cada petici�n del usuario, ser� recibida, en primer t�rmino, por este controlador. Para ello, el Front Controller, necesitar� conocer: � A qu� m�dulo de la aplicaci�n desea acceder el usuario � A qu� modelo hace referencia � Qu� recurso se est� solicitando � y, si se le est�n enviando par�metros (argumentos) a 342 MODELO controlador VISTA FRONT CONTROLLER Application Handler Programador PHP Experto Eugenia Bahit dicho recurso Todos estos datos, Front Controller los recibir� invocando al Application Handler y �ste, a la vez, los obtendr� analizando la URI ingresada por el usuario. El Application Handler obtiene el m�dulo, el modelo, el recurso y sus argumentos, analizando la URI a trav�s de la cual, ha ingresado el usuario. Para que el Application Handler, pueda realizar un an�lisis simple de la URI, �sta, en consecuencia, tambi�n deber�a simplificarse. Para ello, utilizaremos las llamadas Friendly URL (o URL amigables). Configurando URLs amigables para nuestra aplicaci�n Hasta ahora, acced�amos a nuestra aplicaci�n, realizando las peticiones mediante HTTP GET. Con las URL amigables, reemplazamos dicho m�todo, tomando el valor de cada argumento -anteriormente enviado por HTTP GET- y convirti�ndolo en parte de la URL. Ve�moslo con un ejemplo. Por HTTP GET, pasar�amos los argumentos as�: archivo.php? modulo=camisas&modelo=boton&recurso=get&boton_id=32 343 Programador PHP Experto Eugenia Bahit Pero ahora, convertiremos el valor de cada argumento, en parte de la URI: /camisas/boton/get/32 Es decir, que todas nuestras URIs, ahora estar�n conformadas siguiendo el patr�n: / modulo/modelo/recurso/argumento1/argumento2/argument o9/ Para poder trabajar de esta forma, vamos a necesitar: 1. Configurar Apache, habilitando el m�dulo rewrite 2. Modificar nuestro virtual host, permitiendo sustituir las URL 3. Crear un archivo .htaccess Configuraci�n de Apache La configuraci�n de Apache es sumamente sencilla. Lo �nico que tendremos que hacer es habilitar el m�dulo rewrite: sudo a2enmod rewrite Y luego, reiniciar Apache para que el cambio surta efecto: 344 Programador PHP Experto Eugenia Bahit sudo service apache2 restart Modificar el VirtualHost Habr� que editar el archivo VirtualHost de nuestro Web Site. Por defecto, en distribuciones basadas en Debian, este archivo se encuentra en /etc/apache2/sites-available/default En otras distribuciones, este archivo puede localizarse dentro de /etc/httpd/conf.d/ Para modificarlo, dentro de <VirtualHost> tendremos que localizar (o crear si no existe), las etiquetas <Directory /> y </Directory> y all� dentro, colocar el valor de la variable AllowOverride en All, para permitir que las URL sean sustituidas: <VirtualHost> # directivas.... <Directory /> AllowOverride All </Directory> </VirtualHost> A continuaci�n, recargaremos Apache: 345 Programador PHP Experto Eugenia Bahit sudo service apache2 reload Creando el archivo .htaccess El archivo .htaccess lo crearemos en el directorio principal de la aplicaci�n. En �l, nos encargaremos de habilitar la re-escritura con el m�dulo rewrite: RewriteEngine On E indicarle que todo tipo de peticiones, deber�n ser devueltas a un archivo llamado init_core.php que crearemos a continuaci�n: RewriteRule ^ init_core.php Creando un Application Handler Nuestro Application Handler, ser� una clase que tendr� a su cargo: � Analizar la URI � Setear un array con la petici�n del usuario (m�dulo, modelo y recurso) � Setear un array con todos los argumentos que le hayan sido pasados al recurso Para ello, lo que tendremos que hacer es, declarar m�todos 346 Programador PHP Experto Eugenia Bahit que se encarguen de: 1. Obtener la URI 2. Dividirla en fragmentos 3. Asignar cada fragmento a la petici�n m�dulo/modelo/recurso correspondiente 4. Almacenar los argumentos La clase AppHandler # Archivo: /app/core/app_handler.php class AppHandler { private $petition_names; private $uri; private $uri_data; public $peticiones = array(); public $args = array(); function __construct() { $this->petition_names = array("modulo", "modelo", "recurso"); $this->uri = $this->get_uri(); $this->uri_data = $this->set_peticiones(); $this->peticiones = $this->get_peticiones(); $this->args = $this->get_parametros(); } private function get_uri() { return str_replace(APP_PATH, "", $_SERVER['REQUEST_URI']); } private function set_peticiones() { return explode("/", $this->uri); } private function get_long() { $partes = count($this->uri_data); return ($partes > 3) ? 3 : $partes; } private function get_peticiones() { $long = $this->get_long(); $arr = array_combine(array_slice($this->petition_names, 0, $long), array_slice($this->uri_data, 0, $long)); foreach($arr as $clave=>$valor) { if(empty($valor)) unset($arr[$clave]); } return $arr; 347 Programador PHP Experto Eugenia Bahit } private function get_parametros() { return array_slice($this->uri_data, $this->get_long(), count($this->uri_data)); } } Analicemos la clase AppHandler: El m�todo get_uri() retorna el valor de la clave REQUEST_URI del array superglobal $_SERVER. Lo que hace es quitar el contenido de la constante APP_DIR (declarada en el settings) de la URI. Esto es necesario, si nuestra app, no est� corriendo en el directorio ra�z de nuestro servidor. Entonces, declaramos el path hasta el directorio de nuestra aplicaci�n, en la constante APP_DIR y obtendremos la parte de la URI que nos interesa. El retorno de este m�todo, ser� almacenado desde el constructor en la propiedad $uri. private function get_uri() { return str_replace(APP_PATH, "", $_SERVER['REQUEST_URI']); } El m�todo set_peticiones(), se encarga de dividir la URI anterior, tomando como referencia, la barra diagonal. Los valores retornados por este m�todo, ser�n almacenados desde el constructor, en la propiedad $uri_data. private function set_peticiones() { return explode("/", $this->uri); } El m�todo get_peticiones() realiza un an�lisis m�s complejo de los resultados almacenados en la propiedad $uri_data. Primero, llama get_long() para calcular la longitud de las peticiones. Esto es: 348 Programador PHP Experto Eugenia Bahit Si hay 3 valores, estaremos en condiciones de asignar un valor a m�dulo, otro a modelo y el tercero, a recurso. Si hay m�s, ser� se�al de que estamos recibiendo argumentos. Pero si hay menos, la longitud ser� la cantidad de elementos reales de uri_data: private function get_long() { $partes = count($this->uri_data); return ($partes > 3) ? 3 : $partes; } get_peticiones, necesita de get_long, para poder extraer de uri_data, la cantidad correspondiente a los tres primeros elementos (o si hay menos, los que tenga). Mediante array_slice, extrae el m�dulo, modelo y recurso de uri_data (son los tres primeros elementos) y hace lo propio con el array que define los nombres (claves) para estos tres pedidos. $arr = array_combine(array_slice($this->petition_names, 0, $long), array_slice($this->uri_data, 0, $long)); } Finalmente, recorre el array resultante en busca de elementos vac�os. Elementos vac�os pueden suceder cuando la URI real, finalizaba en una �/�. Si encuentra elementos vac�os, elimina la clave del array resultante y finalmente, retorna ese array. El resultado, ser� almacenado desde el constructor, en la propiedad p�blica $peticiones. foreach($arr as $clave=>$valor) { if(empty($valor)) unset($arr[$clave]); } return $arr; Finalmente, el m�todo get_parametros se encargar� de extraer de uri_data, aquellos elementos �sobrantes� (es decir, cualquier elemento que se encuentre del tercero). Esos 349 Programador PHP Experto Eugenia Bahit elementos, ser�n los argumentos que almacene la propiedad p�blica args desde el constructor: private function get_parametros() { return array_slice($this->uri_data, $this->get_long(), count($this->uri_data)); } Conclusi�n Podemos deducir entonces, que la clase AppHandler ser� la encargada de analizar toda la URI, colocando en disposici�n p�blica un array asociativo con las peticiones m�dulo-modelorecurso y otro, con todos los argumentos. Esta clase, entonces, ser� utilizada por el FronController, quien solo la instanciar� y, el m�todo constructor de �sta, le proveer� a FrontController de un objeto AppHandler, con dos propiedades p�blicas AppHandler->peticiones y AppHandler- >args, para que FrontController se encargue del resto, es decir, instancie al controlador correspondiente, envi�ndole la petici�n (recurso) con todos los argumentos que este recurso necesite. La clase FrontController Ahora s�, ya estamos en condiciones de crear nuestro controlador general de la aplicaci�n. La clase FrontController se encargar� entonces, de: � Llamar a AppHandler para obtener las peticiones solicitadas por el usuario y los argumentos enviados � Buscar el controlador necesario e importarlo � Llamar al controlador y enviarle la petici�n (recurso) y los argumentos 350 Programador PHP Experto Eugenia Bahit Veamos como ser� la clase FrontController: # Archivo: /app/core/app_handler.php class FrontController { private static $app_data; public static $error = NULL; public static function run() { self::$app_data = new AppHandler(); if(!self::call_controller()) { self::$error = "El recurso solicitado no se encuentra disponible"; } } private static function call_controller() { extract(self::$app_data->peticiones); if(isset($modulo) && isset($modelo) && isset($recurso)) { require_once("$modulo/controllers/$modelo.php"); $controller = Helper::set_controller_name($modelo); $method = Helper::set_method_name($recurso); $c = new $controller($method, self::$app_data->args); return True; } } } Cuando FrontControll deba ser �activado�, ser� llamado de forma est�tica, su m�todo run. Por ejemplo: FronController::run(); Ser� el encargado de manejar todas las peticiones a nivel de la aplicaci�n. Para obtener las peticiones y argumentos, FrontController simplemente crea un objeto AppHandler que ser� almacenado en una propiedad est�tica (privada) $app_data (l�nea 9 de app_handler.php) 351 Programador PHP Experto Eugenia Bahit self::$app_data = new AppHandler(); Para buscar e importar el controlador necesario, primero verificar� que las peticiones m�dulo, modelo y recurso se encuentren seteadas en el objeto AppHandler y de ser as�, incluir� el archivo del controlador: # convierte en variables temporales todas las peticiones extract(self::$app_data->peticiones); # si modulo, modelo y recurso han sido seteadas... if(isset($modulo) && isset($modelo) && isset($recurso)) { # Importa el archivo del controlador require_once("$modulo/controllers/$modelo.php"); Si nos ponemos detallistas, podremos observar que FrontController, de manera indirecta nos est� forzando a mantener una estructura de directorios y una arquitectura, conforme lo que hemos visto hasta ahora sobre MVC. Finalmente, para llamar al controlador y pasarle el recurso (petici�n) y sus argumentos, hace lo siguiente: Se vale de un Helper para obtener el nombre adecuado del controlador. El mismo debe ser el nombre de la petici�n, sin guiones medios, con formato CamelCase seguido de la palabra Controller. De esto se encarga el m�todo Helper::set_controller_name(). $controller = Helper::set_controller_name($modelo); Luego, hace lo propio con el recurso. Para obtener el nombre del m�todo-controlador del recurso, se vale de otro helper, el cual considerar� las reglas para establecer dicho nombre: ser� el nombre del recurso, reemplazando guiones medios por bajos, todo en min�sculas y seguido del sufijo _controller. 352 Programador PHP Experto Eugenia Bahit $method = Helper::set_method_name($recurso); Finalmente, instancia al controlador, pas�ndole dos par�metros: el recurso y los argumentos: $c = new $controller($method, self::$app_data->args); Conclusi�n Si observamos el c�digo anterior, concluiremos en que FrontController, nos ha guiado el camino para crear nuestros controladores. Los mismos deber�, contar s� o s�, con: � Un m�todo constructor preparado para recibir dos par�metros: el recurso (nombre del m�todo que deber� invocar) y los argumentos (par�metros que deber� pasarle a ese m�todo) � Un m�todo para cada recurso, donde el nombre del mismo, deber� ser nombre_del_recurso_controller � cada uno de esos m�todos, deber� estar preparado para recibir una cantidad incierta de par�metros Y s�. Si est�s pensando en call_user_func_array() y func_get_args(), est�s en el camino correcto :) Vale aclarar adem�s, que todos los archivos que 353 Programador PHP Experto Eugenia Bahit integran el core de la aplicaci�n que fuimos creando a lo largo de este taller, son reutilizables en cualquier tipo de aplicaci�n modular MVC. Esto significa, que para crear aplicaciones MVC modulares, podr�s reutilizar este core y solo concentrarte en crear los m�dulos, con sus correspondientes modelos, vistas y controladores. Es decir, que todos estos archivos del core, no son meros ejemplos que deban ser interpretados y trasladados a los objetivos de tu aplicaci�n, sino que por el contrario, son el n�cleo de tu aplicaci�n, sea cual fuere su objetivo. A continuaci�n, veremos como crear el controlador para nuestros modelos. Creando controladores para nuestros modelos Crear el controlador para nuestros modelos, ser� una tarea sumamente simple. Por un lado, ya conocemos qu� es lo que debe hacer un m�todo-controlador: � Instanciar al modelo � Modificar sus propiedades (cuando sea necesario) � Llamar a uno de sus m�todos (el cual, nos retornar� alg�n dato) � Enviar los datos retornados por el modelo, a la vista 354 Programador PHP Experto Eugenia Bahit Por otro lado, nuestro FrontController, nos ha definido las caracter�sticas que tendr�n que tener nuestros controladores: � El nombre de la clase ser� el nombre del modelo, seguido del sufijo Controller: class ModeloController { } � Deber� tener un m�todo constructor, preparado para recibir dos par�metros. El primer par�metro, ser� el nombre del recurso (m�todo-controlador) y el segundo, un array de argumentos para enviar a ese recurso: class ModeloController { public function __construct($recurso='', $argumentos=array() { } } � La clase ModeloController, deber� contar entonces, con un m�todo por cada recurso, donde el nombre de �ste se conforme por el nombre del recurso en min�sculas, palabras separadas por guiones medios, sucedido del sufijo _controller: class ModeloController { public function __construct($recurso='', $argumentos=array() { } protected function nombre_del_recurso_controller() { } } � El m�todo constructor, deber� ser quien invoque al m�todo-controlador pas�ndole los argumentos 355 Programador PHP Experto Eugenia Bahit necesarios. Para ello, utilizar� la funci�n nativa call_user_func_array: class ModeloController { public function __construct($recurso='', $argumentos=array() { call_user_func_array(array($this, $recurso), $argumentos); } protected function nombre_del_recurso_controller() { } } � Y a la vez, los m�todos-controladores que requieran par�metros, deber�n estar preparados, para recibir una cantidad de argumentos incierta: class ModeloController { public function __construct($recurso='', $argumentos=array() { call_user_func_array(array($this, $recurso), $argumentos); } protected function nombre_del_recurso_controller() { $argumentos = func_get_args(); } } 356 Programador PHP Experto Eugenia Bahit � Y finalmente, nuestros controladores, deber�n almacenarse en un directorio controllers dentro del m�dulo al cual pertenezcan. El nombre, deber� ser el mismo que el delo modelo. Caracter�sticas extras que debemos tener en cuenta para crear nuestros controladores son: � Sabemos que todos los m�todos-controlares (o casi todos), necesitar�n instanciar al modelo. Pues entonces, lo haremos en el m�todo constructor, asignando el objeto a una propiedad: class ModeloController { public function __construct($recurso='', $argumentos=array() { call_user_func_array(array($this, $recurso), $argumentos); $this->modelo = new Modelo(); } protected function nombre_del_recurso_controller() { $argumentos = func_get_args(); } } � Tambi�n sabemos que todos los m�todos-controlares, deber�n instanciar a la vista. As� que haremos lo propio: class ModeloController { 357 Programador PHP Experto Eugenia Bahit public function __construct($recurso='', $argumentos=array() { call_user_func_array(array($this, $recurso), $argumentos); $this->modelo = new Modelo(); $this->view = new ModeloView(); } protected function nombre_del_recurso_controller() { $argumentos = func_get_args(); } } � Ya que el controlador ser� el �nico autorizado a conectar el modelo con la vista, ser� a la vez, el �nico encargado de importar los archivos correspondientes a su modelo y su vista: require_once('modulo/models/modelo.php'); require_once('modulo/views/modelo.php'); class ModeloController { public function __construct($recurso='', $argumentos=array() { call_user_func_array(array($this, $recurso), $argumentos); $this->modelo = new Modelo(); $this->view = new ModeloView(); } 358 Programador PHP Experto Eugenia Bahit protected function nombre_del_recurso_controller() { $argumentos = func_get_args(); } } Lo anterior, entonces, ser� nuestro template para crear controladores. Entonces, con este template, crearemos -ahora s�-, nuestro controlador BotonController: # Archivo: /app/module/controllers/boton.php require_once('module/models/boton.php'); require_once('module/views/boton.php'); class BotonController { public function __construct($recurso='', $args=array()) { $this->boton = new Boton(); $this->view = new BotonView(); call_user_func_array(array($this, $recurso), $args); } private function get_controller() { $args = func_get_args(); $this->boton->boton_id = (count($args) > 0) ? (int)$args[0] : 0; $data = $this->boton->get(); $this->view->get_boton($data); } } Notas adicionales sobre el ejemplo A fin de hacer m�s entretenido|productivo|real nuestra aplicaci�n, haremos una peque�a modificaci�n en el query del m�todo get del modelo Boton, de forma tal que pueda traer un 359 Programador PHP Experto Eugenia Bahit solo resultado que coincida con la ID seteada o todos, en caso de que la ID sea igual a cero. El modelo Boton, entonces, se ver� as�: class Boton { public $material = NULL; public $color = NULL; public $ojales = NULL; public $boton_id = 0; public function save() { $sql = "INSERT INTO boton (material, color, ojales) VALUES (?, ?, ?)"; $data = array("ssi", "{$this->material}", "{$this->color}", "{$this->ojales}"); return DBObject::ejecutar($sql, $data); } public function get() { $sql = "SELECT material, color, ojales FROM boton WHERE boton_id "; $sql .= ($this->boton_id > 0) ? "= ?" : "> ?"; $data = array("i", "{$this->boton_id}"); $fields = array("Material"=>"", "Color"=>"", "Ojales"=>""); DBObject::ejecutar($sql, $data, $fields); return DBObject::$results; } } Agregamos una l�nea que completa el query, diciendo que: Si boton_id es mayor que cero, la cl�usula WHERE se completar� con = ?, sino, se completar� con > ? Completando la aplicaci�n Ahora s�, es hora de ir �redondeando� nuestra aplicaci�n. En principio, haremos lo siguiente: 360 Programador PHP Experto Eugenia Bahit 1. Mover el archivo settings.php al directorio ra�z de la aplicaci�n 2. Crear en el mismo directorio, un archivo llamado init_core.php que -como su nombre lo indica- se encargar� de inicializar el n�cleo de la aplicaci�n. De esta forma, la estructura de nuestra aplicaci�n, deber� verse as�: app/ +-- core/ � +-- app_handler.php � +-- db_object.php � +-- front_controller.php � +-- helper.php � +-- render_data.php � +-- render_template.php +-- init_core.php +-- module/ � +-- controllers/ � � +-- boton.php � +-- models/ � � +-- boton.php � +-- views/ � +-- boton.php +-- settings.php +-- site_media/ +-- html/ +-- lista_botones.html 361 Programador PHP Experto Eugenia Bahit +-- template.html A continuaci�n, haremos algunas modificaciones a nuestro archivo settings.php Por un lado, agregaremos 4 constantes, encargadas de definir: � El path donde corre nuestra aplicaci�n. Esto es, desde el directorio ra�z del servidor, hasta el directorio ra�z de la aplicaci�n inclusive. � El directorio que almacenar� los archivos est�ticos. Si es otro servidor, deber� definirse antecediendo el protocolo correspondiente. Por ejemplo: http://xxx.xxx.xx.xx/app/ � El directorio que almacena los archivos HTML dentro del directorio est�tico. � El directorio del n�cleo (en nuestro caso, core/) const APP_PATH = "/mvcapp/"; const STATIC_PATH = "site_media/"; const STATIC_DIR = "html/"; const CORE_DIR = "core/"; Por otro lado, modificaremos -en tiempo de ejecuci�n- el include_path del php.ini, a fin de poder incluir los archivos de forma m�s c�moda. Asignaremos el valor de la constante superglobal __DIR__ a esta directiva: ini_set("include_path", __DIR__); Finalmente, agregaremos un peque�o script, para que nos muestre todos los errores, avisos y advertencias de PHP, siempre y cuando, no estemos en producci�n (si haz realizado el curso PHP para Principiantes o haz le�do el libro del curso, el 362 Programador PHP Experto Eugenia Bahit script que utilizaremos es el mismo que usamos en el ejemplo del cap�tulo �Tratamiento y Control de Errores�): const PRODUCCION = False; if(!PRODUCCION) { ini_set('error_reporting', E_ALL | E_NOTICE | E_STRICT); ini_set('display_errors', '1'); ini_set('track_errors', 'On'); } else { ini_set('display_errors', '0'); } Inicializador del n�cleo Finalmente, vamos a crear nuestro archivo inicializador del n�cleo, init_core.php Este archivo cumplir� dos funciones: 1) Incluir (importar) todos los archivos del n�cleo en el orden preciso 2) Iniciar el FronController require_once('settings.php'); require_once(CORE_DIR . 'app_handler.php'); require_once(CORE_DIR . 'front_controller.php'); require_once(CORE_DIR . 'db_object.php'); require_once(CORE_DIR . 'helper.php'); require_once(CORE_DIR . 'render_template.php'); require_once(CORE_DIR . 'render_data.php'); FrontController::run(); Para este ejemplo, agregaremos una tercer funci�n que en 363 Programador PHP Experto Eugenia Bahit realidad, es opcional. �sta, ser� la de verificar si FrontController ha fallado, para imprimir un mensaje que lo indique. if(!is_null(FrontController::$error)) { print FrontController::$error; } �Ya tenemos nuestra aplicaci�n funcionando! Para probarla, podremos ingresar en nuestro navegador la URI correspondiente: http://dominio/aplicacion/module/boton/get O podremos tambi�n, pasar una ID de bot�n como argumento: http://dominio/aplicacion/module/boton/get/32 364 Programador PHP Experto Eugenia Bahit 365 Programador PHP Experto Eugenia Bahit Los patrones de dise�o Front Controller y Application Handler en MVC Es posible crear una vista general, que sea mostrada al usuario cada vez que un recurso no sea especificado. Existen varios m�todos mediante los cuales podremos lograrlo. El m�s simple de todos, es crear una clase FrontView a nivel del core. Para ello necesitaremos: 1. Una plantilla HTML 2. Una clase FrontView que herede de RenderTemplate 3. Agregar un m�todo de comprobaci�n a FrontController que se encargue de llamar a nuestra nueva clase Veamos como ser�a en la pr�ctica. Plantilla HTML (site_media/html/default_template.html): <!doctype html> <head> <meta charset="utf-8"> <title>MVCApp</title> </head> <body> <header> <h1>MVC App</h1> </header> <section> <header> <h2>M�dulos Disponibles en la aplicaci�n</h2> 366 Programador PHP Experto Eugenia Bahit </header> <section> <p><b>{FRONT-CONTROLLER-MESSAGE}</b><br/> Por favor, seleccione la opci�n de su inter�s</p> <h3>M�dulo de Camisas</h3> <nav> <b>Botones:</b> <a href="{APP_PATH}module/boton/get">Listar Botones<a> </nav> </section> </section> </body> </html> La clase FrontView (core/front_view.php): class FrontView extends RenderData { public function __construct($msg='') { $this->dict = array("APP_PATH" => APP_PATH, "FRONT-CONTROLLER-MESSAGE" => $msg); } public function show_default_view() { $this->set_data($this->dict); $this->file = STATIC_PATH . STATIC_DIR . "default_template.html"; $this->render_template(); print $this->html; } } M�todo de validaci�n en FrontController: M�todo para setear el mensaje que ser� transmitido a la vista: private static function set_error() { if(isset(self::$app_data->peticiones['modulo'])) { self::$error = "El recurso solicitado no se encuentra disponible"; } } Este m�todo, setear� siempre el mismo error, excepto cuando el usuario acceda a la carpeta ra�z (index) de la aplicaci�n. M�todo que ejecuta un comando �mostrar vista por defecto�: 367 Programador PHP Experto Eugenia Bahit private static function execute_command() { $command = new FrontView(self::$error); $command->show_default_view(); } Ambos m�todos, implicar�n modificar el m�todo run de FrontController: if(!self::call_controller()) { self::set_error(); self::execute_command(); } Eliminar la siguiente validaci�n del init_core.php if(!is_null(FrontController::$error)) { print FrontController::$error; } Y l�gicamente, agregar la importaci�n de front_view.php en el init_core.php require_once(CORE_DIR . 'front_view.php'); 368 Programador PHP Experto Eugenia Bahit Web Services: creaci�n de una API REST Conceptos b�sicos Web Services Un Web Service (o WS) -Servicio Web- es una tecnolog�a que permite a dos aplicaciones diferentes, comunicarse entre s�, por medio de diferentes protocolos como HTTP, FPT, SMTP, SOAP, entre otros. API Una API (Application Programming Interface) -o Interfaz de programaci�n de aplicaciones-, es el conjunto de m�todos de una capa de abstracci�n, que permite a otra aplicaci�n, acceder a funcionalidades que le son ajenas. La API, pertenece a la aplicaci�n que permitir� a otras, hacer uso de una o m�s de sus funcionalidades. REST REST (Representational State Transfer) -Transferencia de estado representacional- es un patr�n arquitect�nico que basa su estructura en recursos (todo es un recurso), permitiendo crear servicios Web que solo utilicen el protocolo HTTP, de manera clara y simple, donde las solicitudes (al igual que en MVC), son manejadas a trav�s de la URI. 369 Programador PHP Experto Eugenia Bahit Entendiendo las Arquitecturas REST A diferencia de otras arquitecturas para servicios Web, como SOAP, por ejemplo, REST propone manejar, tanto los m�todos (PUT, POST, GET y DELETE) como los estados de los mismos (resultado de la petici�n), en el mismo recurso, manejando las peticiones solo a trav�s de las URIs, significando esto, que solo ser� necesaria una �nica petici�n, para acceder a cualquiera de los m�todos. Realmente, una arquitectura REST, no tiene ninguna complejidad. Solo se requiere de una API que maneje las peticiones del cliente y le retorne el recurso solicitado con su estado correspondiente. Caracter�sticas de una API-REST Para manejar las peticiones del cliente y retornar el recurso solicitado con su correspondiente estado, una API solo requiere: 1. Analizar la URI (de la misma forma que lo hace el ApplicationHandler) 2. Actuar como una capa de abstracci�n, entre el recurso solicitado y el cliente (de la misma forma que lo hace un FrontController) 3. Entregar al cliente el recurso solicitado, en formato de 370 API (server-side) Env�a petici�n mediante la URI Retorna recurso y estado CLIENTE (aplicaci�n usuaria del servicio) Programador PHP Experto Eugenia Bahit texto plano (no se utilizan GUIs). Los requerimientos 1 y 2, se dise�an con un patr�n FrontController (la API en s�, ES un FrontController). Y el requerimiento 3, solo necesita un m�todo que imprima los datos entregados -en nuestro caso, por el controlador de nuestra MVCApp-en un formato v�ilido. Por ejemplo, XML o mejor a�n, alo mucho m�s liviano y legible como JSON12. 12 http://es.wikipedia.org/wiki/Json 371 Programador PHP Experto Eugenia Bahit Funcionamiento de la API REST de nuestra MVC App En cap�tulos anteriores, hemos creado una completa aplicaci�n modular, basada en el patr�n arquitect�nico MVC. Veremos ahora, como crear un Web Service para nuestra aplicaci�n, basado en una arquitectura REST. Antes de continuar analizando el c�digo fuente que acompa�a el m�dulo, es importante saber con exactitud, qu� es lo que har� nuestro Web Service y cu�l ser� su finalidad. Objetivo del Web Service El objetivo de nuestro Web Service, ser� el de permitir a otras aplicaciones, obtener los mismos datos que nuestra app provee, pero en un formato que la aplicaci�n cliente, para utilizar para s�. Por ejemplo, cuando nosotros accedemos al listado de botones, obtenemos la lista de registros almacenados en nuestra DB, con una GUI que los formatea: 372 Programador PHP Experto Eugenia Bahit Imaginemos que somos fabricantes de botones, y que uno de nuestros clientes, desea disponer de nuestra base de datos de botones, actualizada y en todo momento. Permitirle a nuestro cliente, una conexi�n externa a nuestra base de datos, ser�a una locura, puesto que no solo significa un inmensurable riesgo de seguridad, sino que adem�s, con cada cambio que hici�ramos a la base de datos, deber�amos estar notific�ndolo al cliente. Hoy, es un cliente. Pero ma�ana �como notificaremos a diez mil clientes? La soluci�n, entonces, es crear un Web Service. Que nuestro cliente, pueda hacer una simple petici�n mediante el protocolo HTTP, y as�, disponer en todo momento, los datos actualizados. De esta forma, nuestro WS, le entregar� al cliente, un archivo JSON, que �ste, podr� manipular al igual que un array multidimensional retornado por una consulta SQL de selecci�n: 373 Programador PHP Experto Eugenia Bahit [ {"Material":"nylon","Color":"verde","Ojales":4}, {"Material":"madera","Color":"caoba","Ojales":0}, {"Material":"madera","Color":"verde","Ojales":0}, {"Material":"metal","Color":"oro","Ojales":0}, {"Material":"pl\u00e1stico","Color":"lima","Ojales":25}, {"Material":"pl\u00e1stico","Color":"lima","Ojales":25}, {"Material":"pl\u00e1stico","Color":"lima","Ojales":25}, {"Material":"pl\u00e1stico","Color":"lima","Ojales":25}, {"Material":"pl\u00e1stico","Color":"lima","Ojales":25}, {"Material":"Madera","Color":"Caoba","Ojales":2}, {"Material":"cart\u00f3n","Color":"amarillo fluorescent","Ojales":4}, {"Material":"pasto","Color":"verde","Ojales":2}, {"Material":"aaaaaa","Color":"aaaaaaaaaa","Ojales":4}, {"Material":"nananana","Color":"nananan","Ojales":1}, {"Material":"mimbre","Color":"beige","Ojales":1} ] Es as� entonces, como nuestro cliente, podr� iterar con los datos de este archivo (incluso, en cualquier otro lenguaje que no sea PHP, ya que JSON, es y puede ser manipulado desde cualquier lenguaje), para presentarlos a sus propios usuarios, de la manera que desee. Por ejemplo, en un formulario: 374 Programador PHP Experto Eugenia Bahit Formato de la URI Al igual que con MVC, la URI de nuestro WS, se conformar� por: modulo/modelo/recurso/parametros La �nica diferencia con MVC, es que todo lo anterior, ser� precedido por la palabra �api� a fin de identificar que se trata de una petici�n de tipo servicio Web: api/modulo/modelo/recurso/parametros Vale aclarar, que los par�metros, son opcionales. 375 Programador PHP Experto Eugenia Bahit Dise�o de la API Recordemos, como bien se coment� al inicio, que la API utilizar� un patr�n de dise�o FrontController, es decir, que deber� ser un objeto (tipo APIController), que utilizando un ApplicationHandler para analizar la URI, se encargue de instanciar al controlador del modelo solicitado y llamar al m�todo correspondiente al recurso solicitado. Finalmente, esta APIController, deber� entregar los datos retornados por el m�todo del controlador del modelo, en formato JSON al cliente. Ahora s�, analicemos el c�digo que acompa�a al m�dulo. 376