You are on page 1of 96
Ajax, un juego de nifios Crea tus propias aplicaciones Web Primeros pasos Ajax, Javascript, CSS Un ejemplo sencilla: correo Ajax Llega mas lejos Editor HTML, editor de imagenes JavaScript avanzado, aplicaciones en 00037 — IMM 8414090037754 Numero 37 - Ajax, un juego de nifos - 5,00 Euros 2 PC Cuadernos AJAX, un juego de nifios John Maxwell Revision: David Bosman y Stéphanie Guillaume Traduccién: Victoria Pefiafiel © Copyright: KnowWare EURL PC Cuadernos - Basicos www.pe-cuadernos.com Sociedad editora: KnowWare E.U.R.L. 70, rue Georges Villette F-77250 Ecuelles Francia Director: Mikkel Franck Direccién electronica: info@po-cuadernos.com Depésito legal: B-43100-2007 ISBN: 2-915605-82-3 Imprenta: Imprintsa impressions intercomarcals, S.A. Sant Fruit6s de Bages, Barcelona Distribucién quioscos: Coedis 8.L. Avda. de Barcelona, 225 Molins de Rei Barcelona Venta por numeros: | Después de su aparicién, los ntimeros de esta coleccién pueden pedirse por correo. | (Consulta la pagina 93) Internet no s6lo es la biblioteca mas grande jamds creadas, con cientos de millones de pa- ginas accesibles desde un navegador. Tam- bién es un espacio de trabajo a tiempo com- pleto, con herramientas para la publicacién de paginas Web, para la creacién de archivos Word o Excel, presentaciones, etc. Es perfec- tamente posible trabajar desde Internet, usan- do un navegador. En este cuaderno no presentaremos este tipo de aplicaciones (ya lo hicimos en un titulo an- terior, "Web 2.0 para todos"), sino que el autor propone una iniciacién a Ajax, uno de los mé- todos actuales de desarrollo mas populares. A través de una serie de ejercicios practices y progresivos, el autor, un apasionado del tema, muestra como utilizar JavaScript, PHP o Perl para crear verdaderas aplicaciones en linea. Explica cémo ofrecer a los usuarios herra- mientas que sean a la vez simples, intuitivas y potentes. Al terminar la lectura, el lector quizas no lo sabra todo, pero sabra lo suficiente para crear sus propias aplicaciones, incluso de ni- vel avanzado. El autor nos invita a un viaje apasionante, un viaje de descubrimiento de una tecnologia puntera que podria perfectamente transformar la Web tal como la conocemos. Y revolucionar nuestra forma de trabajar. Atencién, para sacar el maximo partido de es- te cuadermo se necesita una base sdlida de programacién. Es posible intentar seguir los ejemplos sin haber programado antes para In- ternet, pero sera una tarea dificil. Antes de lan- zarse a devorar este titulo, es conveniente que el lector refresque sus conocimientos de HTML, CSS y Javascript. También seria bue- no conocer PHP y Perf Mikkel Franck, editor. mfranck@pe-cuadernos.com Todos los productos citados en este manual son marcas registradas o marcas comerciales. E/ autor y el editor declinan toda responsabili- dad que pueda surgir de la utilizacién de los datos o programas que aparecen en este libro. Prélogo 3 Prélogo Si tienes este manual entre las manos, significa que te interesa AJAX. Quizés seas un erudito, un especialista de la Iiada de Homero... y esperas que esta publi- caci6n te ofrezca un anilisis a fondo del céle- bre guerrero, Desgraciadamente, este no es el caso, excepto quizas por el simbolismo del nombre, que su- giere fuerza y simplicidad Si, como probablemente sea tu caso, lo que te interesa es la tecnologia Web AJAX, entonces no estés solo. Todo el mundo habla de AJAX, es la nueva palabra de moda. En el émbito de la programaci6n para Internet, existen pocas novedades que supongan un avance tan in- crefble. Aunque las paginas totalmente estati- cas hace tiempo que son cosa del pasado, las paginas Web dindmicas siempre se han ejecu- tado en el lado del cliente. En la actualidad, una sola pagina Web se puede comunicar con el servidor directamente, sin el laborioso pro- ceso de tener que recargarse o cargar otras pa- ginas. Esto significa que el usuario puede acceder di- rectamente a cualquier comando que ponga- mos a su disposicién, casi como si hubiera ins- talado el site como aplicacién local. ¢Quién, en el mundo del desarrollo Web, podrfa ignorar una posibilidad como esta? Modestia aparte, la mejor forma de aproximar- sea AJAX es el manual que aqui se presenta, ya que esté plenamente en linea con la esencia misma de AJAX. En el mercado se pueden encontrar manuales de AJAX cuyo primer ejemplo funcional es un monstruoso cédigo que ocupa diez paginas, envuelto en 30 paginas de explicaciones. Si uno logra abrirse camino entre tal selva de complejidad, seré recompensado con otras doscientas paginas de texto, que exigen un es- fuerzo hercuileo para su lectura y més atin para encontrarles una aplicacién préctica. Esta qui- zs sea la mejor forma de aprender a pilotar un helicéptero, pero sin duda no es adecuada para aprender AJAX. AJAX es una tecnologia ligera y flexible, y asi es como debe ser abordada. En este manual, el primer ejemplo cabe en una pagina de cédigo, y se puede copiar y ejecutar en menos de cinco minutos. Después de leer diez. paginas de este cuaderno, tendrés una sélida idea de los fun- damentos. Serds capaz de construir tus propias aplicaciones, observar cémo evolucionan a la velocidad de la luz, partiendo como una chispa y terminando como una gran llama. Esta es sin duda alguna la mejor forma de aprender; na- die enciende un fuego con un céctel Molotov. Después de trabajar con este manual, habrés adquirido un buen conocimiento de la tecno- logia AJAX que te permitird lanzarte a proyec- tos mas complejos, adaptados a tus objetivos particulares. AJAX se habré convertido en un arma més de tu arsenal, y este manual te servi- 14 como guia de consulta. En conelusi6n, AJAX es una tecnologia simple que permite crear proyectos muy ambiciosos, Igual que el antiguo Ajax: simple, pero fuerte. John Maxwell 4 Fundamentos Fundamentos AJAX es un estilo de programacién que permi- te al desarrollador hacer que una pagina Web interacttie con el servidor Web que la aloja. Normalmente, una pagina Web solamente dis- pone de un método para comunicarse con el servidor, que consiste en recargarse a ella misma o cargar otra pagina. Si utilizamos AJAX, una vez que la pagina se ha cargado puede ejecutar comandos en el servidor, recibir cualquier informaci6n resultante de dichos comandos y mostrarla al usuario sin necesidad de recargarse. Aplicacién Web tradicional ® (one resultado: | devuelto a ta nueva pagina Las ventajas de este tipo de programacién sal- tana la vista. La velocidad de la interaccién es mucho mayor, especialmente en el caso de aquellos sitios que tienen muchos graficos 0 contenido interactivo, puesto que ya no es ne cesario recargar imagenes o scripts. La pagina no se refrescard, sino que todo funcionara in si- tu. Las pesadas interfaces actuales se pueden sus- tituir por sistemas rapidos, reactivos y mucho més eficaces. Para todo tipo de aplicaciones en linea, desde editores Web hasta btisquedas en bases de da- tos, desde programas de correo hasta foros, AJAX se ha convertido en el método dptimo para proporcionar al usuario las ventajas de la funcionalidad de tu servidor. Las aplicaciones nunca habfan estado tan cerca del nivel de eje- cuci6n de los programas instalados localmente. Alinventor dé la denominacién AJAX, Jesse James Garrett, se le ocurri6 el nombre en la ducha, m4s 0 menos como Arquimedes, que descubrié el principio de la densidad mientras estaba en la baiera. AJAX son las siglas de Asynchronous JavaScript And XML, es decir, XML y JavaScript Asincro- nos, donde el término “asincrono” indica que la tecnologia se ejecuta en su propio marco temporal. CeCe eM AC TeLL AJAX es una filosofia, no es un conjunto de aplicaciones ni un lenguaje de programacién concreto. El sistema AJAX esté compuesto por cuatro elementos: JavaScript, el objeto XMLHttpRequest, una aplicaci6n del lado del servidor y CSS. James Garrett En pocas palabras, el equipo del cliente ejecuta una serie de comandos JavaScript , incluida una llamada al objeto XMLHttpRequest. Dicho 4Céomo funciona? 5 objeto envia informacién al servidor en un en- cabezado HTTP, y el servidor procesa la in- formaci6n y ejecuta la accién solicitada. Si es necesario devolver informacién al equipo cliente, el objeto XMLHttpRequest se encarga de recibirla y mostrarla con la ayuda de JavaS- cript y CSS. En este manual se presupone que el lector esta familiarizado con el lenguaje HTML y los fun- damentos de la programacién Web, como apli- caciones JavaScript sencillas y un poco de de- sarrollo del lado de servidor. De todos modos, haré un esfuerzo por explicar cémo funciona cada uno de los elementos poco a poco, hasta lograr una comprensién global de AJAX. Aplicacion Web AJAX peticion XHR. enviada al servidor 21 servidor procesa ta informacion resultado enviado a XHR resultados mostrados en la misma pagina utilizando CSS JavaScript JavaScript es una tecnologia que permite a los programadores escribir funciones como parte de un archivo HTML. Por lo general se incluye en la seccién de la pagina Web, y se ejecuta en el ordenador del usuario. JavaScript puede realizar céleulos complejos y mostrar el resultado en la pagina Web. Tam- bién permite cambiar elementos dentro de una pagina Web en interaccién con el usuario. Pue- de acceder a informacién del sistema del usua- rio y mostrar los resultados. Con AJAX, JavaScript puede comunicarse con el servidor Web y mostrar la informaci6n resultante. Este es un ejemplo de pagina HTML que con- tiene una funcién JavaScript simple: Haz clic aqui Cuando el usuario hace clic en el vinculo de texto, el evento onClick inicializa la funcién message () y aparece un cuadro de didlogo. Los comentarios se incluyen entre los signos ayes, La sintaxis es muy similar a la de Java, aunque, en sentido estricto, estos dos lenguajes no estan relacionados. Sin embargo, al igual que Java, JavaScript utiliza la programacién orientada a objetos, lo que nos lleva al siguiente apartado. XMLHttpRequest Abreviado como XHR, es un objeto que pro- porciona al programador toda la funcionalidad necesaria para que el ordenador cliente se co- munique con el servidor Web. 6 Fundamentos La programacién orientada a objetos es un esti- lo de programacién en el que, en lugar de uti- lizar variables simples, los programas utilizan bloques de informacién que no s6lo pueden almacenar un grupo de valores sino un conjun- to de instrucciones, conocidas como métodos. El objeto XHR tiene un conjunto de métodos muy titiles, de los cuales los mAs importantes son los siguientes: open(method, URL, async) Este método prepara el objeto para la transmi- sién de informacién al servidor. Se puede defi- nir con una serie de tipos, como POST, GET y PUT, La mayor parte del tiempo, utilizaremos PosT: es el método més utilizado para enviar informacién a partir de formularios HTML. La ‘URL contiene la aplicacién a la que se enviara la informacion. La propiedad async puede ser true o false. Si es false, indica al objeto que debe esperar a recibir una respuesta del servidor an- tes de continuar procesando el c6digo JavaS- cxipt. Sies frue, se puede procesar el cédigo mientras se espera la respuesta del servidor, por ejemplo para mostrar al lector un mensaje "Su peticiGn esté siendo procesada...” hasta que el servidor devuelva el resultado. setRequestHeader (label, value) Este método permite indicar al objeto qué tipo de texto le enviaremos, especificdndolo en el encabezado HTTP. Label suele ser casi siem- pre “Content-Type”, y se pueden elegir una se- tie de tipos, incluidos los formatos XML y HTML, 0 un conjunto de resultados de un for~ mulario. send (content) Este método transmite informacién textual al servidor. De momento, esto es todo lo que necesitamos saber sobre los métodos. Las propiedades si- guientes también estén asociadas con el objeto XHR: readyState El valor de esta propiedad se modifica en fun- cin del estado del objeto XHR: seré 0 si no es- td inicializada, 1 si esta abierta, 2 si esté envia- da, 3 si recibe, 4 si esta cargada. Este tiltimo va- lor es el més util, lo utilizaremos muy pronto. onreadyStateChange Esta propiedad provoca una accién cada vez que el readystate del objeto cambia responseText Esta propiedad toma el valor de la informacion textual que devuelve el servidor. Por medio de JavaScript, preparamos el objeto | XHR con toda la informacién necesaria, lo en- | viamos y recibimos los resultados en la pro- | piedad responseText. A continuacién, utili- | zamos el método getElement ByTd de JavaS- cript para insertar el resultado en alguna parte del documento. | Aplicacién del lado del servidor 2Qué aplicacién del lado del servidor podemos utilizar para procesar la informaci6n enviada por el XHR? En realidad, cualquier aplicacién que pueda procesar formularios se puede uti- lizar para AJAX, lo que por definicion se tra- duce en cualquier servidor de aplicaciones, desde JSP hasta ASP, desde los servidores Pro- log hasta el legendario “potato-powered ser- ver”. En os ejemplos de este cuaderno, utilizaremos las dos aplicaciones libres més utilizadas: PHP y Perl, bajo Linux. Précticamente cualquier maquina del mundo puede ejecutar como mf- nimo una de estas aplicaciones, y su cédigo es intercambiable de una plataforma a otra. Los ejemplos mas importantes, hacia el final del cuaderno, se basan en PHP 5; comprueba que tienes esta versiOn o una més reciente. Sino tienes PHP 0 Perl en tu sistema, puedes descargarlos de los sitios Web siguientes: htto://www.php.nevdownloads.php http:/www.perl.com/download.csp ‘También es posible desarrollar aplicaciones paralelas en el lenguaje que més te convenga, ajustando tus algoritmos a los ejemplos de este cuaderno, La aplicaci6n siguiente es un ejem- plo de la facilidad de lectura de PHP: El servidor recibe el nombre del usuario desde un formulario HTML, le saluda por su nombre 4Cémo funciona? 7 y devuelve la fecha. Es tan facil de leer que re- sulta incitil comentar el cédigo. Adaptar aplicaciones como esta a Ruby, ASP 0 JSP es relativamente sencillo. Como veremos, las paginas Web que utilizan AJAX obtiene re- sultados practicamente idénticos independien- temente de la aplicacién de servidor utilizada. Con AJAX, el servidor puede hacer cualquier cosa para el usuario que le permitan hacer sus aplicaciones. Conexiones de base de datos, biisqueda de imagenes, movimientos de bra- 20s robotizados... cualquier cosa que el servi- dor sea capaz de hacer. También puede expo- ner el servidor a ataques maliciosos, por lo que es un riesgo que deberemos tener en cuenta a lo largo de este manual. CSS (Cascading Style Sheets) C3S.es el lenguaje que se utiliza para aplicar reglas de estilo a los documentos HTML, por ejemplo sobre fuentes y posicionamiento. Con una tecnologia nueva como AJAX, es esencial presentar correctamente los resulta- dos. Por ese motivo, la practica comin es utili- zat CSS para dar formato a la informacién que devuelve la propiedad responseText. Cuando JavaScript utiliza el método getElement ByTd para insertar los resultados de una peticidn AJAX en una pagina Web, uti- liza el campo id de un elemento HTML como pueden ser
0 , para identificar nde debe colocarse el texto. También pode- mos asignar un campo clags a cualquier ele- mento de este tipo, y aplicarle un estilo deter- minado con ayuda de CSS. Veamos un ejemplo de archivo con un elemen- to
preparado para AJAX:
Y ahora llega el momento de pasar de la teoria a la practica con nuestro primer ejemplo de AJAX... 8 Un ejemplo en una pagina: correo AJAX Un ejemplo en una pagina: correo AJAX En las paginas que siguen, crearemos una apli- cacién AJAX que permitiré al cliente enviar un mensaje al webmaster de una pagina Web sen- cilla, dejando su correo electrénico como direc- cién de respuesta, La aplicacién servird perfectamente como for- mulario de opinién, y presenta la ventaja de que la direccién del webmaster permanece oculta, de forma que los robots de spam y phishing no podrdn encontrarla. De todos modos, es una aplicacién limitada. Limitada por tu imaginacion: con un poco de creatividad deberfas poder, desde el momento en que ésta sea funcional, ampliarla segiin tus intereses. Mi nico consejo para desarrollar la aplicaci6n es que cualquier cambio deberfa realizarse de forma gradual. Es aconsejable probarla antes y después de cada modifica- cion, y cada version deberia guardarse por se- parado en caso de que sea necesario volver atras. Preparacién del XHR Ena pagina siguiente encontrarés el cédigo completo de nuestro archivo HTML ajax_mailer.html. Para empezar por el final, observa que el elemento
no tiene un campo action. El motivo es que los resulta- dos del formulario serén tomados por JavaS- cript cuando se produzca el evento onClick provocado por la accién del usuario de hacer clic en el bot6n enviar. El botén input “Enviar” ejecuta la siguiente Hamada de objeto: amlhttpPost ("/mailer.php") El archivo mailer.php debe encontrarse en el servidor Web, en el mismo directorio que ajax_mailer. html. Esta Hamada inicia el proce- so AJAX, en el transcurso del cual el objeto xmlhttpPost () recibe la direccién de nues- tra aplicaci6n de servidor. Sin embargo, toda- via queda mucho por hacer antes de contactar con el servidor. Necesitamos declarar especifi- camente un objeto de la clase XMLHttpRequest (). Si pasamos al principio del archivo, necesita- mos hacer varias cosas para preparar nuestro XHR, En primer lugar, debemos comprobar que no existe ninguna instancia en memoria de un objeto con el mismo nombre, por lo que aplicamos el valor false a xm1HttpRea (el nombre que asignaremos a nuestro objeto). A continuacién, utilizaremos dos funciones pa- ra declarar una instancia de XMLHttpRequest (), de forma correcta segtin el navegador del cliente. Desgraciadamente, Internet Explorer no reconoce el objeto XHR hasta la version 7. Las versiones anteriores exi- gen una forma de declaraci6n distinta. Para resolver este problema, basta con com- probar si existe la clase window . XMLHttpRequest. Si existe, asigna- remos xm1Ht tpReq a dicha clase de la forma normal: self.xmlHttpReq = new XMLHttpRequest () ; Sila clase no existe, deberemos utilizar el con- trol ActiveX de Microsoft para declarar axmlHttpReq. Un ejemplo en una pagina: correo AJAX

Tu direccién:
Mensaje:
<1!---llama XHR--->

10 Un ejemplo en una pagina: correo AJAX Hemos dicho que declaramos el objeto de la forma normal, pero seguramente habrés detec- tado algo anormal en la declaracién anterior a menos que seas un programador orientado a objetos muy experimentado. ¢Para qué puede servir se1£? Hemos declara- do este elemento antes de la instruccién i, de Ia siguiente forma: var self = this; A partir de este punto, precede cada vez.a xmLHt tpReq. Puede parecer un ejercicio intitil pero, en realidad, es la forma estandar de re- solver el problema conocido como loss of scope. Simple y llanamente, utilizaremos nuestro ob- jeto xm1HttpReq en distintos contextos, dentro y fuera de funciones. Existe el riesgo de que en algtin punto, especialmente si preten- demos ampliar la aplicacién para convertirla en algo mas grande, de repente el objeto se considere a si mismo como totalmente nuevo y recién inicializado. A partir de entonces se produciran errores y las variables no definidas provocarén el caos. Atribuyéndole la etiqueta self, podremos garantizar que la misma ins- tancia del objeto am1HttpReg es la que apare- ce cada vez Ahora que hemos inicializado el objeto XHR, podemos empezar a equiparlo. Para ello, em- pezaremos usando el método XMLHttpRequest .open. Asignaremos al XHER tres elementos de informaci6n: el méto- do, la direcci6n de la aplicacién y el modo asincrono. El método es POST, porque envia- remos los resultados de un formulario HTML, y este es el método establecicio. Como direc- cién de la aplicacion indicaremos strURL: es Ia informacion recibida del botén del formulario HTML. Hemos asignado su nom- bre en la declaracién de la funcién xmlhttpPost. Contiene la cadena de caracte- res "/mailer.php", el nombre de nuestra apli- caci6n del lado del servidor. En lo que respecta al modo asincrono, el valor es true porque necesitamos que el objeto XHR controle per- manentemente el valor de readyState mien- tas el servidor trabaja: self.xmlHttpReq.open('POST', strURL, true); Ya hemos indicado al objeto XHR cémo, dén- de y a qué ritmo debe actuar, y a continuacién Ie diremos sobre qué informacion debe actuar. La forma més facil de expresarlo es decir “pa- res de elementos de formulario, etiquetas y va- lores”. La forma compleja, y es la que debemos utilizar para el XHR, es indicar las siguientes dos cadenas de texto: ‘Content-Type', 'application/x- www-form-urlencoded! El método setRequestHeader recibe estas dos cadenas, y ya puede esperar los resultados del formulario HTML. Debemos pasar una tiltima instruccién al obje~ to XHR antes de poder realmente enviar la in- formaci6n. Debemos indicarle donde y cuando actuar sobre la informacion que devolverd el servidor. En primer lugar, cundo: queremos que el XHR ejecute una accién en el momento en que el servidor devuelva la informacién so- licitada, en este caso una confirmacién de que el mensaje del usuario ha sido enviado. Pode- mos expresarlo utilizando su propiedad readyState: cada vez que el estado cambie (es decir, a cada instancia de onreadyStateChange) comprobar si readyState ha alcanzado el valor 4 (carga- do). Una vez cargada la respuesta, mostrar la informacion. Aqui es donde resulta «til contar con la varia- ble se1£; de lo contrario podrfamos habernos topado con el problema de loss of scope en la funcién llamada por onreadyStateChange, ya que esta funcién es llamada varias veces seguidas con un valor de readystate dife- rente cada vez, Cuando la condicion se cumple, readyState es 4 y la informacién solicitada ha sido recil da del servidor. Entonces el XHR realiza la si- guiente llamada: updatepage (self. xmlHttpReq. respons eText: Un ejemplo en una pagina: correo AJAX 1 Como ya hemos explicado anteriormente, responseText es la propiedad que contiene la respuesta del servidor. La funcién updatepage() ; se define al final de la sec- cién JavaScript de nuestro documento, y ense- guida nos ocuparemos de ella. Entretanto, el objeto XHR esté listo para la ba- talla; ya sabe cémo, dénde y cuando debe ac- tuar y qué debe hacer después. Lo tinico que tenemos que hacer es darle los resultados del formulario y enviarlo: self.xmlHttpReq. send (getquerystrin 30); La funcién getquerystring() se encarga de recuperar la informacién del formulario HTML, como veremos enseguida. Hasta aqui llega todo lo que debemos saber sobre el objeto XMLHttpRequest. Ahora nos queda hablar de las dos funciones JavaScript Manipulacién en JavaScript Para preparar la informacién que el usuario ha introducido en el formulario HTML, debemos recurrir a JavaScript para manipular el texto resultante, de modo que esté preparado para el método POST que hemos elegido. El encabezado POST exige que los resultados del formulario se envien de la forma siguient 2nom- bre=valor&nombre2=valor2é&nombre3=valor3 Aqui, el nombre corresponde al campo name de cada o CI ol ‘rene Gear yer Maori) Marcadores Heramietas Aide © ‘Actuaizacssn: Utilizando el mismo marcador que antes, en este caso el contenido de news.txt sera enviado por news.cgi, gracias a la instruccién *g* En PHP, el elemento edit form se construye de la siguiente forma: Actualizacién:
La variable $content, utilizada de esta forma, mostrard el contenido de news.txt. Cabe subrayar que el contenido de news.txt, si contiene etiquetas HTML, no tendra el mismo aspecto cuando se muestre en el elemento
de la seccién anterior y en el elemento , la salida sera texto sin formato. Del mismo modo, los caracteres de salto de linea y espacios mtilti- ples sern respetados por el elemento
La versién PHP es idéntica, excepto porque la seccién anterior de PHP sustituye el marcador LINKEDLIST y la llamada xmihttpPost esta destinada a createfile.php. El formulario HTML es muy sencillo, contiene un cuadro de entrada de texto para el nombre del archivo que se escribird, y un campo texta- rea para el contenido del archivo. . Con la lista leida que se muestra justo encima, el resultado deberia ser parecido a este: Ejemplos AJAX 25 Escribiry relistar E ic Has lefdo la nota anterior? _ Como ya hemos dicho, el objeto XHR sera exactamente el mismo que el que hemos utili- zado en los dos ejemplos anteriores. La funcién getquerystring () serd similar a otros ejem- plos: function getquerystring() { var form = document . forms ['createform'] ; var target = form.target.value; var story = form.story.value; qstr = 'target=' + escape (target) + 'kstory=! + escape(story) ; return gstr; } La variable target contiene el nombre de ar- chivo seleccionado por el usuario, Podria re- sultar itil sugerir al usuario el uso de la exten- si6n de archivo apropiada: .txt para archivos de texto, xml 0 .html para los lenguajes co- rrespondientes, etc. En el servidor, tenemos la sencilla tarea de es- cribir el nuevo archivo de texto y después relis- tas el contenicio del directorio. En ambos casos, Perl y PHP, el codigo para crear de nuevo la lista del directorio es idéntico al cédigo que hemos usado antes, por lo que se puede copiar y pegar. En la versién Perl, la lista de enlaces se transmite directamente al XHR, en lugar de insertarse en news.html por sustituci6n de la cadena. He aqui la versién Perl: #1 /usr/bin/perl use CGI; $query = new CGI; $target = $query->param('target'); $story = $query->param('story'); $dir="../html/ajax/"; Stxtdiragdir."txt/™; $reldir="../ajax/txt/*; $newfile = $txtdir.$target; # escribe en el archivo open (OUTPUT, "> $newfile"); print OUTPUT "Sstory"; close OUTPUT: # mostrar la lista del directorio Igual que antes, el signo > es un comando para escribir en un archivo. Los tres directorios son Jos mismos que en el archivo Perl anterior, de modo que el cédigo de creacién de la lista fun- cionaré correctamente. En la versién PHP, necesitamos la funcién stripslashes () igual que en el caso del edi- tor de texto en linea, para asegurarnos de que no queden caracteres de escape en el texto: No nos ha parecido necesario utilizar las fun- ciones de codificacién utf-8 para mantener los caracteres especiales en esta aplicacién, pero en funci6n del sistema operativo podria ser una buena idea. Consulta la seccién anterior para saber cémo hacerlo. 26 Crea in de archivos de texto En este punto, es muy importante comprobar que los atributos de la carpeta /txct estén co- rrectamente definidos: en Linux, eso suele sig- nificar CHMOD 777. ‘Todavia es més importante que los directorios por encima de /tzxt estén definidos como CHMOD 755 u otro valor de alta seguridad. Piensa que un usuario podrfa insertar la si- guiente cadena como nombre de archivo: .-/index.htm1. Dicho de otro modo, seria un intento de reescribir la pagina principal de tu sitio Web. Mostrar el resultado Una vez que el objeto XHR ha recibido una respuesta y la nueva lista del directorio, se in- voca la funcién JavaScript updatepage () . Es- ta completard nuestra aplicacién, puesto que la nueva lista se muestra al usuario junto con un mensaje que indica que el proceso ha finaliza- do. function updatepage (str) { document .getElementByTd ("resultado ") innerHTML = str; document .getElementBytd ("mensaje") sinnerHTML = "MIS A JOUR"; var form = document. forms ['createform! } Se envia un mensaje al elemento
contenido en create.html y create.php. La variable stx contiene la lista en- lazada de nombres de archivos que ha devuel- toel XR, y que sustituye la antigua lista, con- tenida en el
. Esta es la secci6n correspondiente a create.html:
<{---LINKEDLIST-
Elelemento
contiene una llamada a una clase CSS, que se define a continuacién: En pocas palabras, significa que todo el texto utiliza la fuente Verdana pero los enlaces son rojos y sin subrayado. El mensaje al usuario también es rojo. Protégete en PHP En Perl, no es posible ejecutar un script en un directorio en el que el servidor Web tenga ac- ceso de escritura, Sin embargo, PHP autoriza que sus archivos se ejecuten desde directorios no protegidos. Para comprender el potencial que tiene esta brecha de seguridad para ti y tus hackers po- tenciales, utiliza tu aplicacién de texto para crear el siguiente archivo, al que llamaras ins- pect:php: Si haces clic en el enlace a inspect.php, obten- drds una pagina con informacién sobre el ser- vidor web, con todos los médulos y detalles de la versién de PHP. En si mismo no supone un gran riesgo de seguridad, pero no es dificil imaginar las otras posibilidades que quedarian disponibles para un hacker experimentado si esta puerta esté abierta. Ejemplos AJAX 27 Por ello es vital comprobar que los directorios tengan los atributos correctos. Esta es la reac- cin de PHP si intentamos escribir en un direc- torio superior, bien protegido: ar oe wiemiincney name ATanac ss Theme) pacerwesher/igt/ gloss com/Mml/ajan/teterante create Se deniega el permiso de escritura y no se pue- de crear el archivo. Aqui, un intento de cambiar los permisos de un directorio también falla. Es posible utilizar el editor de textos para crear un archivo lla- mado chmod. php, que contenga una instruc- cién para cambiar los atributos de /txt. Sin embargo, este es el resultado cuando abrimos chmod. php: ane oe Se tenet ppnsees teams > 8 OR tama OG Warning chinod0: Operation not permited in Aromelinaxvell;public hinljax'txtoops.php online? PHP no permite el uso de chmod () a través del servidor Web. A pesar de estas precauciones, es vital que el acceso a esta aplicacién quede restringido me- diante nombre de usuario y contrasefa Tu turno Esta aplicacién esté limitada en muchos aspec- tos (aunque en otros, como ya hemos visto, es demasiado abierta). Por ejemplo, siempre se muestra el mismo mensaje al usuario tanto si se ha escrito el archivo como si no. Podriamos aftadir una seleccin de mensajes para avisarle de que el archivo no se ha podido escribir, otro avisando de que la carpeta no se puede encon- trar, y otro para confirmar que el archivo se ha modificado correctamente. En cuestion de seguridad se podrian mejorar muchas cosas. Por ejemplo, podrfamos buscar caracteres en los nombres de archivo que po- drian ser indicativos de intenciones dudosas. Podriamos buscar y prohibir el carécter ”/”, que indicaria un intento de escribir en otro di- rectorio distinto del de destino. 28 Eliminacion de archivos Veremos cémo hacerlo en el proximo capitulo. De hecho, podriamos prohibir todos los carac- teres exceptuando los alfanuméricos 0 “.”. Tes- ricamente se podria hacer al nivel de JavaS- cript, antes de se envien los datos al XHR, pero los hackers podrian superar la dificultad escri- biendo su propia instruccin POST en la barra de direcciones del navegador, por lo que seria mejor hacerlo en el servidor. La siguiente aplicacién sera un proyecto que podria perfectamente estar relacionado con es- te. Siesta aplicaci6n sirve para crear archivos, la siguiente serviré para eliminarlos. Desde un navegador como Mozilla, podremos abrir apli caciones con pestaftas separadas, afiadir archi- vos desde una y eliminarlos desde la otra. Esta funcionalidad puede ser muy util para realizar pruebas. Eliminacion de archivos en el Sei lls En esta seccién, crearemos una aplicacién que te permitira especificar un directorio concreto en el que el usuario tendré permiso para eli- minar archivos, De la misma forma que el ejemplo anterior se basaba en la funcién opendix () , este depen- dera de unlink () pero, en esta ocasién, am- pliaremos un poco el XHR y més concretamen- te desarrollaremos las funciones JavaScript a las que lama. Si la aplicaci6n anterior abria la puerta al abu- so, no hay que ser muy listo para imaginar lo que podria hacer un programa para eliminar archivos. Sin embargo, limitaremos la posibili- dad de entrada que tiene el usuario: lo tinico que podra y deberd usar son casillas y un bo- t6n de enviar. Especificaciones funcionales La principal diferencia es que la anterior apli- cacién era productiva, y esta es destructiva. En lugar de aftadir elementos a una lista de archi- vos, crearemos una lista y después la reduci- remos. E] resultado final sera mas sencillo para el usuario final, pero en realidad es més dificil de desarrollar que los ejemplos anteriores. Para empezar, por cada elemento de nuestro directorio de destino necesitaremos una casilla, que el usuario deberé activar cuando desee climinar el archivo que apareceré junto a la misma, ya sea un archivo o cientos de ellos. En. si mismo, esto seria bastante sencillo, pero ca- da casilla debe tener el nombre correcto y estar asociada al archivo junto al que aparece. En segundo lugar, para que la aplicacién del lado del servidor funcione correctamente, de- bemos indicarle cudntos elementos hay en la lista en la que debe buscar los archivos para eliminar. De lo contrario, tendria que compro- bar cientos de archivos aunque s6lo hubiese dos en la lista. Eso significa que deberemos enviar un valor adicional al XHR, la cantidad total de archivos. EI XHR ser ligeramente distinto esta vez, aunque se basard en la versién anterior de los otros tres ejemplos. Ademés del valor adicio- nal que le enviaremos, utilizaremos la propie- dad readyState para mostrar un mensaje al usuario durante la ejecucién en el servidor, que sera sustituido por un mensaje definitivo cuando se haya completado la peticién. La funcién JavaScript getquery () también sera mas compleja, puesto que discriminaré los detalles necesarios de los innecesarios, y sola- mente enviaré las instrucciones esenciales al servidor. Estas son las especificaciones de nuestra apli- caci6n de eliminacién de archivo: Ejemplos AJAX 29 Mostrar una pégina con una lista de archivos del directorio de des tino, con una casilla junto a cada uno. Cuando se envie el formulario, AJAX enviaré una peticién al ser- vidor para eliminar todos los ar- chivos marcados por el usuario. 1 usuario ver& un mensaje temporal. Bl servidor recibiré el nimero to- tal de archivos de la lista y los comprobaré todos hasta el total, desvinculando los que estén marca- dos para su eliminacién. Se mostraré una confirmacién de la operacién, y una nueva lista con un nuevo conjunto de casillas. Gestion de un numero variable de casillas Las casillas de verificacién funcionan de la si- guiente forma: una casilla llamada foo posee dos propiedades importantes. Una propiedad textual, £00.name y una propiedad booleana foo. checked, cuyo valor es true o false segtin si el usuario ha hecho clic en Ia casilla 0 no. Teéricamente, podriamos haber hecho lo si- guiente: mostrar una serie de casilla, cada una con el mismo nombre de un archivo del direc torio de destino. Sin embargo, esta aproxima- cién conllevaria problemas. La aplicaci6n del lado del servidor recibirfa un encabezado POST parecido a este: ue. txt=trueginifinity.js-falses lemonsqueezer.html=truesquantum.pl =falsegstop=4", Esta informacién seria muy dificil de procesar porque la aplicacién del servidor puede no te- ner ni idea de cudles serén los nombres de va- riables antes de recibirlas. Quizs serfa posible pedirle al servidor “elimina cualquier cosa que tenga el valor true” pero de este modo nos arriesgamos a una gran catéstrofe. En muchos lenguajes de programacién, incluido Perl, to- das las variables inicializadas se consideran true tant mientras no se establezcan como false 00. En este ejemplo, aplicando esta técnica el sis- tema intentaria eliminar un archivo llamado “stop” porque la variable stop, que contiene el ntimero total de archivos, tiene un valor 4 y en consecuencia es true. Un método mejor consistirfa en gestionar todas las casillas utilizando una estructura de con- trol. Mientras establecemos la lista de conteni- do del directorio podemos contar los elemen- tos con ayuda de una sencilla variable $n, que se convertiré en $n++ (es decir, $n més uno) por cada pasada del bucle. La misma variable se puede enviar al XHR y al servicor como ntimero total de archivos. Podemos utilizar $n para nombrar las casillas, aunque necesitare- mos que el ntimero vaya precedido de otro ca- récter, ya que las convenciones de nombres de variables no permiten el uso de un ntimero como nombre de variable. La primera casilla se lamaré delete. form._1, con un valor ini- cial de false, la segunda se llamaré _2, ete. Para asociar cada casilla con un nombre de ar- chivo del directorio de destino, también utili- zaremos $n para nombrar un elemento de formulario , que contendré el nom- bre del archivo correspondiente. Los elementos de formulario ocultos almacenan datos, pero son invisibles para el usuario. En consecuencia, junto a delete. form._1 tendremos delete. form._1text, cuyo valor seré el primer nombre de archivo que devuelva la funcién readdir (). En pseudo-cédigo, el bucle for serd algo asi: n igual a 1; por cada archivo de la carpeta checkbox = _n; hidden = _nText(contiene el nombre de archivo) ; mostrar el nombre de archivo con enlace al archivo; net; 30 Eliminaci6n de archivos Esta es la seccién de delete-php:
" ."$file" "
"; gne+; } } closedir ($dh) ; } } echo ""; 2 ) '>
Ejemplos AJAX Ei) Las instrucciones opendir () deberian sonarte del ejemplo anterior, igual que las comproba- ciones destinadas a identificar el directorio ac- tual y el superior. El valor de $n se incrementa con cada bucle de Ia instruccién “while”, y cuando se ha proce- sado el tiltimo archivo del directorio, éste se cierra y §n se envia al XHR mediante una se- gunda instruccién en la llamada a Ja- vaScript. La instruccién echo sirve para mostrar una se- rie de resultados como aqui: inifinity. je
Como vemos, a cada elemento tomado de la lista del directorio se le asignard una casilla con un nombre apropiado y el elemento oculto correspondiente. En la versi6n Perl que se muestra a continua- cién, la cadena de la casilla es idéntica a la ver- sién PHP, excepto por el uso de $re1dix para el enlace. La instruccién foreach puede sustituir la ins- truccién while que usamos en PHP porque actia sobre una array, no sobre la funcién readdix (). Ambas instrucciones producen el mismo resultado. Aqu{ tenemos delete.cgi: #1 /usr/bin/perl $dir="../html/ajax/"; Stxtdiragdir."/txt"; $reldirs"../ajax/txt/ #construye y muestra una lista fenlazada de archivos del directorio destino con casillas #icon nombres correctos opendix (DIR, $txtdir) ; my @list=readdir (DIR); closedir (DIR) ; Sresult=""; $n=1; foreach my $file (@list){ if (($file ne ".") && ($file ne "..")) { $result=$result "cinput type=\"checkbox\" name=\"_§n\">" "sa href=$reldirgfile> $file" -"cinput type=\"hidden\" name=\" $n". "text\" value=\"$file\">
"; $ne+) } i Spage=$dir. "delete. html"; $path = "$page"; $page_size = -s $path; open(PAGE, "$path"); read (PAGE, $page, $page_size); close (PAGE) ; $page =~ s/ /$xesult/gi; $page =~ 8//$n/gi; print "Content-type: text/html \n\n"; print "$page"; $result se inserta en el archivo delete.html, que es idéntico a delete.php excepto porque los marcadores de la lista de casillas y el recuento sustituyen a las instrucciones PHP, y la llama- da al XHR contiene el archivo de destino dele- tefile.pl. Ajustes del XHR Desde la primera linea ya se puede apreciar la diferencia en la versién modificaci6n de nues- tra querida funcién XHR. En esta ocasién, no enviamos una sino dos variables a xmlhttpPost (): la cadena que contiene la in- formacién del formulario y stop, el ntimero de elementos de nuestro directorio de destino. La llamada a getquerystring() invocaa stop, de modo que la informacién se reenvia. 32. Eliminacién de archivos También hemos adaptado la funcién asociada ala propiedad onreadystateChange. Por cada cambio del valor de readyState hasta el final, es decir hasta que readyState alcanza el valor 4, se envia un mensaje al usuario para indicarle que la aplicaci6n esté procesando su peticién. Cuando termina la llamada al servi- dor, el valor de readyState es 4 y se muestra el mensaje final al usuario, igual que en los ejemplos anteriores. Esta es la funcién XHR para delete.html en Perl, y delete.php: function xmlhttpPost (strURL, stop) { var xmlHttpReq = false; var self = this; if (window.XMLHttpRequest) { /* l'objet XHR, sauf dans IE */ self.xmlHttpReq = new XMLHttpRequest (); else (window.Activexobject) { /* XBR dans IE seulement */ self.xmlHttpReq = new Activexobject ("Microsoft .XMLHTTP") } self.smlHttpReq.open('POST', strURL, true); self .2mlHttpReq. setRequestHeader (‘Content-Type', ‘application/x-www-form- urlencoded'); /* formulaire HTML */ self. xmlHttpReq. onreadyStateChange = function() { Af (self. smlHttpReq. readyState <4) { processing ("PROCESANDO. /* mensaje temporal */ } else { updatepage ( self.xmlHttpReq.responseText) /* mensaje final */ } } ys self.xmlHttpReq.send (getquerystring (stop) ) ; /* envio completo */ } La que sigue es la funci6n processing () que envia los mensajes al usuario, que se dirige a un elemento
cuyo identificador es id=alerta. function processing(str) { document .getElementById("alerta") . innerHTML = str; /* alerta al usuario */ } Programacion avanzada en JavaScript Los pares nombre/valor que se envian al XHR a través del formulario tienen una propiedad muy titil: estén indexados. En consecuencia, podemos gestionarlos uno a uno, y eliminar la informacién innecesaria antes de enviarlos al servidor. En pseudo-c6digo, la idea seria: tomar casilla _1 si su valor es true, enviar la cadena _ltext=[nombre archivo] al servidor para que lo elimine si su valor es false, rechazarla tomar la casilla 2 y asi todo el rato hasta la casi- lla _stop (la Gltima de la lista) En la practica, ser mucho més complicado de lo que parece. Para empezar, no podemos simplemente espe- cificar _1++ y esperar obtener _2. Tendremos que incrementar el nimero primero, y después afiadir el resto de la cadena al resultado. Ademés, y esta parte es la dificil, tenemos que transformar _1 en un nombre de variable realmente utilizable. ¢Qué significa eso? Hecho n°l; tenemos que referirnos a form. _1.checkedy form._1text.value, utilizando_1y _1text como nombres de va- riable. Ejemplos AJAX 33 Hecho n°2: cuando creamos las cadenas “. “_1.text”, son variables de pleno derecho. Pregunta: gcmo convertimos las variables en nombres de variables? Respuesta: depende de la implementacién. En Perl, bastaria con enviar la nueva variable a una funcién como si su contenido fuese un nombre de variable. En PHP, es necesario usar el simbolo “variable variable”: $$. En JavaS- cript, se utiliza la funci6n eval () ;. Utilizare- mos las tres funciones, empezando por eval();. Para explicarlo de forma sencilla, eval () pide a JavaScript que realice una tarea sencilla y devuelva el resultado. En la instruccién si- guiente, le pedimos a JavaScript que construya a cadena “_1” y coloque el resultado en la va- riable whether: var whetherseval(*form._'. *1', "checked! ; Podemos comprobar el valor booleano de whether: sila casilla_1 ha sido activada, whether seré true. De lo contrario, sera false. Seha convertido en £orm._1.checked. Nuestra implementacién funcionara de la si- guiente forma: por cada valor de go (empe- zando por 1 y terminando cuando sea equiva- lente a stop) una cadena ind tomaré el valor de un guién bajo y el valor actual de go (_1, _2,_3, etc.), mientras que handle tomaré el valor del nombre de archivo correspondiente (Atext, 2text, 3text, etc). Utilizando la funcién eval (), what accedera ala informaci6n de entrada contenida en form._1text.value y whether accederé a Ja informacién de su casilla en form._1.checked. Si whether es true, se construiré una cadena gstz del tipo “ Ltext=blue.txt&”, Esta construccién se crea mediante handle + + what + '&'. Observa el signo ampersand, que iré seguido del siguiente archivo a eliminar o, si es el tilti- mo de la lista, por una instruccién del tipo stop=4. De este modo, la instruccién POST siempre tendré el formato correcto, ya sea en forma de una instruccién sencilla con la variable stop, o de una cadena de instrucciones separadas por ampersand. function getquerystring(stop) { var form = document . forms ['deleteform'] ; var go = var qstr = ''; while (goparam('stop'); Stxtdire"../html/ajax/txt/"; $reldir="../ajax/txt/"; print $query->header; $go= # xecorre la lista de nombres # de archivos posibles enviada # por el XBR y elimina # los que estan incluidos while ($go<$stop) { $filename = '_'.$go.'text'; if ($query->param ($filename) ) { victim = $query->param ($filename) ; if ($victim =~ m/\//){ print "Accién denegadacbr />"; # prohibe el cambio de # directorio else { Starget = $txtdir.gvictim; unlink ($target) ; print "El archivo §victim ha sido eliminadocbr />*; y i Sgorts } # sigue la nueva lista de # directorio El resto es casi idéntico a delete.cgi, pero en lugar de insertar el resultado en delete.html sustituyendo la cadena, éste se envia al XHR con ayuda de la funcién print (). Después de la lista del directorio llega el final del formula- rio, que se sustituye con un recuento correcto (nuevo): Ejemplos AJAX 35, print " "; Es importante colocar la secci6n del directorio después de la eliminacién, de modo que los ar chivos eliminados no aparezcan en la nueva lista. if ("pecera" =~ m/pez/) significa: “Qe cera contiene pez?”. Su valor es true. if ("../index.html" =~/\//) significa: “La cadena contiene un /?” Su valor es true. Elssigno \ es para el filtrado de /. El equivalente en PHP es muy parecido, pero la formulacién de los nombres de variables y la comprobacién de la presencia de barras inver- tidas es algo distinta. La variable _1text es asimilada y toma el va- lor $impending. La variable $victm se for- ma utilizando el simbolo variable variable, $$impending. Esto nos permite pedir a PHP que establezca $vict:imen el valor de cual- quier variable cuyo nombre sea igual al conte- nido de $impending, Sila versin actual de $victimno es null, (en otras palabras, si ha sido enviada con un nombre de archivo por el XHR) el nombre de archivo que contiene se eliminara. Una vez mas, se mostrara un men- saje de eliminacion, Aqui se aplica la misma condicién que en Perl en lo que respecta a las barras invertidas. En este caso, utilizaremos la funcién de posicién de cadena strpos ()..Siel resultado es true, no se ejecutaré ninguna accién y el usuario (hacker) recibiré una advertencia. Este es el principio de deletefile.php: /* recorre la lista de nombres de archivo posibles enviados para su eliminacién, desenlaza los que figuran en la lista */ $dir = "./txt/"; Sgo=l; while ($go<$stop) { $impending = "_".$go."text"; $victim = §$impending; if (14s _null($victim)) { if (strpos ($victim,"/") == false) { //no cambio de directorio $target = $dir. $victim; unlink ($target) ; echo "El archivo $victim ha sido eliminadocbr />"; } else { echo "Accién denegada }
} $go+; } /* sigue nueva lista del directorio */ El resto del archivo es idéntico a la secci6n de delete.php, con el siguiente cédigo aftadido al final: echo ""; 2 Si queremos probar la eficacia del filtro de ba- rras invertidas, escribiremos la siguiente linea en la barra de direcciones del navegador: delete.php?_ltext=../go.txt&stop=1 Este es el equivalente en Perl, bajo la forma de una linea de comandos: perl deletefile.pl _itext=../index.html stop=1 En este punto, ya hemos logrado eliminar los archivos, y la confirmacién y la creacién de la lista del nuevo directorio van de camino al XHR. 36 Eliminaci6n de archivos Confirmacién con estilo La tiltima acci6n de nuestra aplicacién es la llamada del XHR a la funcién updatepage (), que tomaré el relevo después de que el servi- dor confirme la finalizacién de la tarea en cur- 80, function updatepage (str) { document .getBlementByTd ("mensaje") -innerHTML = str; /* actualiza la lista */ processing ("COMPLETADO") ; } Dado que el elemento
contenia la lista del directorio original con las casillas, se actualizara para contener la nueva lista, menos los archivos que se hayan elimi- nado entretanto. ion iiss como estore at Para lograr un ahorro (discutible) de tiempo de procesamiento de JavaScript, el mensaje de fin del proceso se envia mediante la funcién processing () que hemos definido previa~ mente. Sustituye al mensaje anterior
install GD Desde Windows: Ena linea de comandos, escribe: PPM install GD Programaci6n orientada a objetos Por muchos motivos, en este ejemplo seré inte- resante utilizar los aspectos de la programa- cidn orientada a objetos de nuestros dos len- guajes de programacién. Para empezar, deja el proyecto abierto a la posibilidad de usar la misma funcionalidad en varias paginas. Ade- més, la programacién orientada a objetos per- mite afiadir 0 eliminar métodos segtin sea ne- cesario, tras lo cual son increfblemente faciles de usar. En tercer lugar, las propiedades de portabilidad y herencia significan que la parte de nuestro proyecto del lado del servidor se podria trasladar fécilmente a cualquier otro lugar, o integrarla en proyectos mas ambicio- sos. Eltinico inconveniente de la programacién orientada a objetos es que presenta el riesgo de parecer demasiado compleja. Este ejemplo pre- tende ser una introduccién sencilla para aque- los que nunca han usado el modelo de objetos anteriormente, como minimo en la versién PHP. En Perl, las cosas se complican un poco. Perl no integra la programacién orientada a objetos del mismo modo que otros lenguajes de pro- gramaci6n, tiene su propio sistema de médu- los. Estos funcionan de modo parecido a otros tipos de programacién orientada a objetos, pe- ro con sus particularidades especificas. De hecho, es habitual que los programadores de Perl siempre hagan las cosas de forma distinta. Carga de la imagen Puesto que AJAX es una tecnologia basada en texto, no tiene forma de cargar archivos bina- ios. Podriamos simular la subida de archivos con AJAX usando Iframes, una posibilidad que cabria la posibilidad de explorar. En ese caso, te remito a esta pagina: hito:/iww.airdweb.comifiles/upload/ in embargo, puesto que no es una técnica AJAX propiamente dicha, utilizaremos un formulario “ala antigua”. En realidad, el proceso de carga es muy senci- Ilo, corte a cargo del lenguaje HTML estandar. Una vez que el archivo se ha transferido, sigue un procedimiento algo mas complicado para colocar el archivo alli donde nos interese, pero no es nada dificil. Empezaremos con la version Perl. Formulario de transferencia Esta es la primera pagina que verd el usuario: Arche aiar yer Wigtoral Menderes Havramengae Ayuss «> 8 Cargar archivo —_— Aware Termindae 40 Editor de imagenes Y aqui el cddigo correspondiente, en el archivo imageperl.html: Editor de imagenes AJAX carga un archivo jpg:

Aqui el elemento importante es: . Este es el que mostrar el bot6n "Examinar” si el usuario es espanol, “Browse” si es inglés, etc El elemento oculto que especifica MAX_FILE_SIZE define el limite del tamafio que el usuario puede cargar. Es una herramienta muy ditil. En este caso, hemos especificado un tamafio maximo de un megabyte. En este punto, es crucial observar dos puntos: T) Lacaxpeta que recibird los archivos carga- dos debe disponer de los permisos de ar- chivo correctos. Intenta dejarla con el mi nimo necesario, aunque algunos servido- res pueden exigir CHMOD 777. Recuerda que los archivos cargados deben ser acce- sibles para la ejecucién de nuestros scripts. 2. Ya lo hemos dicho en varias ocasiones, y no lo volveremos a repetir: comprueba la seguridad de la aplicaci6n. Si estas des- arrollando para una red abierta, protege la pagina de carga con contrasefta, o crea el proyecto en una red local sin acceso exte- rior. Cuando el usuario haya elegido una imagen JPG en su ordenador y haga clic en el botén Cargar archivo, el formulario enviaré el nombre del archivo (como texto) y el propio archivo (como binario) al servidor. Debemos organizar la recepcién. El c6digo siguiente muestra cémo trataremos elarchivo una vez que éste sea recibido por el servidor. En primer lugar instanciamos el mé- dulo CGI para la manipulacién del formulario, y después creamos dos cadenas: el directorio de destino y la ruta de la pagina Web que mos- trard la imagen cargada. A continuacién, utilizamos el método param () de CGI para acceder al nombre del archivo y el método upload () para acceder al archivo en si mismo, utilizando cada vez. el nombre “picture”, tal como lo definimos pre- viamente en el formulario HTML. Carga de la imagen 41 #1/usr/bin/perl -w use CGI; Supload_dir="../html/ajax/uploads" Spath="../html/ajax/uploader.htm"; $query = new CGI; $£ilename= Squery->param("picture"); $upload_filehandle= $query->upload ("picture") ; $filename =~ s/.*[\/\\](.*)/$1/; open UPLOADFILE, *>$upload_dir/$filename"; binmode UPLOADFILE; while ( <$upload_filehandle> ) { print UPLOADFILE; } close UPLOADFILE; $page_size = -s $path; open (PAGE, "$path") ; read (PAGE, $editor, $pa close (PAGE) ; $editor=~s/!---IMAGE---//g; $editor =~ s/1---LOCATION--- /$upload_dir\/$filename/g; print $query->header; print $editor; El c6digo incluye una expresion regular ‘compleja que, en pocas palabras, significa “quitar las barras y cualquier cosa entre barras del nombre del archivo”. Algunos navegado- res incluyen la ruta completa en el nombre de archivo, incluido el arbol de directorios. Con esta instruccién eliminaremos la informacién no deseada, que inevitablemente se incluye en- tre barras. Para representar las barras inverti- das debemos usar un cardcter de escape: barras normales. Elcomando open UPLOADFILE, con el sim- bolo >, indica a Perl que abra un archivo para escribir en nuestro directorio de destino. Po- drfamos usar cualquier nombre para manejar os archivos, pero la convencién es utilizar UPLOADFILE. Puesto que Perl es esencialmen- te un lenguaje orientado a texto, debemos indi- carle explicitamente que el modo de archivo sea binario utilizando la instruccién binmode. La instruccién whi.1e lee el archivo transferido linea a linea, y obliga a escribirlo linea a linea, Los paréntesis angulares leen una linea de S$upload_filehandie (la imagen) en cada pasada el bucle, y el resultado se escribe en UPLOADFILE. Una vez més, volvemos a en- contrar los orfgenes textuales de Perl, puesto que el comando de escritura es print. Una vez cerrado el archivo se crea una variable $edi tor. Le asignamos el contenido de nues- tra pagina Web de destino, en la que se inser- tardn el nombre y el enlace de la imagen car- gada, de la forma que ya conocemos. La pagina de destino, uploader: htm, hace algo més que mostrar la imagen que el usuario aca- ba de transferir. También contiene nuestro XHR y el formulario que el usuario utilizaré para editar la imagen. Sin embargo, de mo- mento examinaremos las secciones que se en- cargan de recibir el archivo transmitido.

Cambio de tamafio: enbsp; Anchura:
Marco:
!---IMAGE---
42 Editor de imagenes Como vemos, la seccién de uploa- der.htm no es demasiado complicada. Contiene una tabla con dos columnas. En la segunda columna encontramos el marcador que el script de Perl utiliza para insertar la imagen. La imagen esta contenida en un
llamado result. Gracias a esta dispo- sicién, el XHR puede sustituirla por la nueva imagen cuando la manipulacién ha terminado. También se encuentra dentro de una seccién llamada picture que permite contro- lar mediante CSS cualquier informacién tex tual sobre la imagen, como la anchura 0 el ta~ maiio de archivo. Ena primera columna de la tabla, encontra- mos un formulario compuesto de dos elemen- tos de entrada: un par de botones de opcién que ofrecen al usuario la elecci6n entre dos ti- pos de manipulacién, Cambio de tamafio para modificar el tamafio de la imagen y Marco para afadir un borde alrededor de la imagen. Junto ‘a Cambio de tamafio, un campo de texto permi- te que el usuario introduzca la anchura de la imagen deseada. El formulario también contiene dos valores ocultos. El primero, Location, contiene el nombre del archivo cargado, tal como lo inser- t6 el script de Perl. El segundo es £i1ename, y est vacio hasta que se carga la pagina. Una vez.cargada, el evento onboad de la etiqueta ejecuta un script llamado randomstring(). ‘Como veremos a continuacién, este script ge- nera un nombre aleatorio para el archivo JPG. Este nombre se envia al servidor a través del XHR, y una vez editada la imagen se guardara con el nombre generado aleatoriamente. Vea- mos el script: function randomstring() { var chars = "9123456789abcdefghiklmnoparstuvwx yz" var string length = 8) var randomstring = ''; for (var is0; icstring length; ise) { var cnum = Math. floor (Math. random() *chars.length) ; randomstring += chars. substring (rnum, rnum+1) ; } var result-randomstring + '.jpg document . forms ['imageform']. filename.value = result; } La cadena chars contiene todos los caracteres alfanuméricos, a partir de los cuales se genera- 14 el nombre del nuevo archivo (con ocho ca- racteres). El bucle £ox genera un nombre alea- torio inferior o igual a la longitud de chars multiplicando dicha longitud por un ntimero aleatorio real comprendido entre 0 y <1 (Wath. random ()). A continuacién, redondea el nimero al valor entero més proximo (Wath. £100r ()). La variable randomstring toma un valor de caracter aleatorio por cada ciclo del bucle for hasta que se convierte en una cadena de ocho cifras. Se le afiade la ex tensién “jpg” y se envia al elemento oculto del formulario. En PHP el proceso es ligeramente distinto. El archivo imagephp.html es idéntico a imag. perl.html a excepcién del formulario, definido con action=uploader .php. El archivo uploader.php contiene el XHR, el script ran- domString y la tabla con el formulario imageform y la imagen cargada. Carga de la imagen 43 Esta es la seccién del archivo:
Cambio de tamafio: énbsp; Anchura:
Marco:

"; }
La primera parte del formulario es idéntica a la versi6n Perl, pero PHP accede al archivo car- gado a través de otra ruta. Para llegar al nom- bre del archivo cargado tenemos que acceder a la variable global $_ PLES, que es una matriz asociativa. En pocas palabras, el nombre del archivo que se ha cargado se almacena en $_PILES['picture'] [ ‘name'], donde picture es el nombre del elemento especificado en el formulario de carga. Para establecer el valor oculto Location de la imagen cargada, utilizamos este valor como direccién, precectido del nom- bre de carpeta uploads. La funcién basename () “quita” la informacion de la car- peta, igual que hacfa la expresién regular de la versi6n Perl, La imagen en sf misma se encuentra en $_FILES['picture'] ['tmp_name']. Para trasladar el archivo al directorio correcto basta con usar la funcién move_uploaded_file() que, como seguro que habrés imaginado, se escribié exactamente para esto. Ahora que ya hemos logrado que los dos len- guajes de programacién se encarguen de reali- zar la transferencia, veremos cual es el plan para el resto de la aplicacién. Plan de ataque Uno de los principales objetivos de un pro- gramador es lograr que las cosas complicadas parezcan sencillas. Una vez terminada, nuestra aplicacién parecer sorprendentemente senci- lla de cara al usuario: cargar un archivo, selec- cionar una funci6n de edicién y hacer clic en ella, y contemplar cémo la imagen cambia. Sin embargo, bajo la superficie hay cierto grado de complejidad, de la que nos ocuparemos ense- guida. Hasta ahora, el usuario ha cargado una imagen en el servidor, éste la ha colocado en la carpeta “uploads” y ha enviado el nombre de la ima- gen a nuesira pagina uploa- der.htm/uploader.php. La pagina ha generado un nuevo nombre de archivo para la imagen modificada, asi que todo esté listo para que en- tre en juego el XHR. 44 Editor de imagenes Lo que ocurrira a continuacién es que el usua- tio seleccionara un tipo de modificaci6n para aplicarla a la imagen. De momento tenemos dos tipos de ajuste: cambio de tamajio, para el que debe especificarse una nueva anchura, y marco, que coloca un marco negro alrededor de la imagen. ELXHER recuperaré la informacién relevante (nombre antiguo y nuevo de la imagen, mo ficacién deseada y detalles) y la enviaré a nuestra aplicacién de servidor, conversion.pl en Perl y conversion.php en PHP. Estas aplicaciones llamaran a un médulo y una clase llamados imageMaster, en un archivo se- parado, para realizar las funciones necesarias, en la imagen cargada. El resultado se guardara en el nombre de archivo especificado por randomString() En esta versidn de la aplicacién, el siguiente paso serd eliminar la imagen original. :Por qué? Precisamente porque no hay ningtin momento claro en el que deba eliminarse la imagen. Es importante que la eliminacién se produzca en algiin momento, de lo contrario un usuario que cargue una imagen de 2 megabytes habré cargado 20 megabytes en el servidor s6lo por editarla 10 veces. Si trabajase sobre diez imagenes, de pronto tendriamos 200 megabytes, una quinta parte de un gigabyte, totalmente intitiles en el servi- dor, sin ningiin método sencillo para liberar el espacio, Sin embargo, seria posible retardar el proceso de eliminacién. Podrfa resultar ttil si, por ejemplo, queremos que el usuario pueda vol- ver atrds en el proceso de edicién de la imagen para deshacer los cambios aplicados en cada etapa. Una variante como esta deberia incluir la eli- minaci6n de los archivos de imagen no desea- dos en algiin momento, sin tener que esperar que el usuario lo recuerde. Una vez que la nueva imagen ha sido creada y se ha eliminado la original, el XHR mostraré el resultado al usuario, reemplazando la imagen original por la nueva. Se llamaré a la funcién randomString () para insertar nuevamente un nuevo nombre de archivo como elemento coculto en el formulario HTML. Al mismo tiem- po, el nombre de la imagen antigua también se sustituird por la nueva. En este punto, la aplicaci6n habré realizado un circuito completo, y esté lista para seguir ade- lante. La pagina de carga ya tiene una imagen, y un nuevo nombre para ia siguiente version de la imagen. Asi, el usuario puede cargar su imagen y tra- bajar en ella tantas veces como dese, mientras cada imagen se guarda en el servidor y susti- tuye la anterior Elhecho de que la edici6n de la imagen se rea- lice con un médulo/clase resulta til en el con- texto de este proyecto. Significa que, a medida que desarrolles la aplicaci6n, puedes afiadir més y més funciones escribiendo un nuevo método en el médulo/clase. Ezitor de imagenes con Peri A continuacién, nos ocuparemos del XHR pro- piamente dicho, y veremos como estas instruc- ciones se transmiten al servidor. Conversion de la imagen 45 XHR para la edicion de imagenes Aligual que en aplicaciones anteriores, el XHR es el mismo en Perl y en PHP. Aftadiremos un elemento destinado a informar al usuario, igual que en la aplicacién de eliminacién de archivos. Esta es la parte del XHR que reaccio- naa la propiedad onreadystatechange: self.xmlHttpReq.onreadystatechange = function() { if (sel£.xm1HttpReq. readystatec4) { processing('go'); } else { updatepage (self. 2mlHttpReq. responseText) ; processing('stop'); } } Antes de la funcién querystring () (véase més adelante), definimos una variable JavaScript llamada method, como aqui: var method; querystring () establecerd esta variable en el método de manipulacién de la imagen que haya elegido el usuario. En JavaScript, las va- tiables declaradas fuera de una funci6n se pue- den usar en ella, siempre que no se vuelva a declarar el mismo nombre de funci6n. En nuestra funcién processing (), podemos re- cuperar la variable method y utilizarla en un mensaje simple para el usuario. Cuando readyState sea inferior a 4, el usuario ob- tendré un mensaje “PROCESANDO", y cuan- do alcance ese valor, borraremos el mensaje: function processing(status) { if (status=="go") { document .getElementById ("process") sinnerHTML = "PROCESANDO! 4method+"..." else { document .getElementById ("process") sinnerHTML ="; } } El mensaje se envia a un elemento
si- tuado justo debajo de la imagen. Esta es la funcién getquerystring(): function getquerystring() { var form = document . forms [ mageform'] ; var target = form. filename.value; var size = form.width.value; var source = form.location.value; if (form.method [0] .checke { var method="resize"; } if (form,method [1] . checke true) | true) | { var method="frame"; } af (((1>size) || (size>3000)) ee {method=="resize')) { size=300; alert('No se ha especificado un tamafio. Establecido a predeterminado: ' + size);} qstr= 'targets' + escape(target) + ‘gsizes' + escape(size) + 'gsource='! + escape(source) + ‘emethod=' + method; return gstr; } Este cédigo presenta algunas sutilezas que exi- gen una explicaci6n. En primer lugar, el con- cepto de botén de opcién. Como podemos ver en la ilustraci6n de la pagina anterior, el usua~ rio puede elegir entre dos funciones de edicién utilizando un botén de opcién. Acceder a la in- formacién de un botén de opcidn es relativa- mente complicado. A diferencia de las casillas, donde cada una tiene su propia identidad, los botones de opcién asociados comparten una misma identidad. Si examinamos a fondo el formulario edit form, comprobaremos que los botones de opcidn asociados a cada funcién tienen el mismo nombre. De hecho, en lo que respecta a JavaScript, representan dos partes de un solo elemento, £orm.method. El primer elemento (método resize) es form.method [0], el segundo (frame) es £orm.method [1] . Los dos botones son ele- mentos de una matriz (las arrays siempre em- piezan por 0) y cada uno tiene un valor checked de tipo booleano, es decir su valor puede ser true 0 false. Si el usuario ha hecho clic en la funcién de re- dimensionamiento, form.method [0] .checked sera true y pues- 46 Editor de imagenes to que s6lo se puede seleccionar una funcién cada vez (ya que los botones de opcién son ex- clusivos) form.method [1] . checked sera necesariamente false. En estas condiciones, podriamos eliminar la segunda instruccién if y utilizar else para definir el método en frame (marco). Sin em- bargo, puesto que esta aplicacién est pensada para ampliarse, parece més légico dejar las co- sas tal como estan para que se pueda afadir method [2], method [3] y siguientes ms adelante. La tercera instrucci6n 4£ procesa los valores limite de la anchura de la nueva imagen defi- nida por el usuario. Si, por error, el usuario no indica ningiin valor de anchura, se cumplira la primera condicién: (1>size). Este caso no deberia presentarse muy a menudo, ya que por defecto el valor se ha fijado en 200 en edi tform, De todos modos, seguro que habré alguien que intentaré escribir un 0 como anchura (lo que nos conduciria a una divisién por 0 més ade- ante, como veremos en el método de cambio de tamaiio), por lo que es preferible evitar errores en el servidor arreglando el problema en origen, Si el usuario ha especificado una nueva anchu- ra superior a 3000, la condicién también se cumpliré, para evitar abusos del espacio de servidor (una imagen en color de 5000 pixeles puede ocupar mas de 10 megabytes). La terce- ra condicién comprueba que el método selec- cionado sea Cambio de tamafio. De no ser asi, no sitve de nada la comprobacién. Si una de las dos primera condiciones (| | sig- nifica una u otra) se cumple y Ia tercera lo con- firma (68), se ejecutard el contenido de la ins- trucci6n if. Lo que ocurre a condici6n es muy sencillo: la anchura se establece en 300 y el usuario recibe una alerta sobre el error cometido. El mensaje de advertencia serd el siguiente: [ts pn cn mv://womdilassicom dics] hort name it ° eam Para terminar, se genera la cadena de la solici- tud y se envia al servidor a través del XHR. Conversion de la imagen Eneste punto de la aplicacién, el servidor ya dispone de toda la informacién que necesita. La imagen antigua est4 esperando en el direc- torio uploads, el XHR ha enviado su nombre, igual que el nombre de la nueva imagen y la informaci6n sobre qué funci6n debe ejecutarse. Empezaremos por estudiar la versi6n Perl, puesto que su método de creacién de objetos es més complejo. Una vez que hayamos comprendido la versi6n Perl, el método empleado por PHP sera mas. facil de entender. Conversién de la imagen 47 Implementacién del médulo Perl Veamos el archivo imager-pl, que recibe la ca- dena de la consulta transmitida por el XHR: #1/usr/bin/perl use CGI; use imageMaster; $query = new CGI; print $query->header; my $source = Squery->param("source") ; my $newwidth = $query->param("size") ; my $target = Squery->param("target") ; my $mathod = $query->param ("method") ; $edit = new imageMaster; $edit->source ($source) ; Sedit->target ($target) ; if ($method eq 'resize') {Sedit->size(§newwidth) ; $edit->resample();} if ($method eq ' fram {S$edit->frame();} $edit->delete(); ) Este cédigo es bastante sencillo: los cuatro pa- rametros se leen con ayuda del médulo CGI y se invoca una nueva instancia del médulo ima- geMaster. Se establecen los dos nombres de ar- chivo, el antiguo y el nuevo, y se invoca el mé- todo deseado. Para terminar, se elimina el ar- chivo original. La simplicidad de este archivo se compensa con la complejidad de imageMaster.pm, el se- gundo médulo de Perl que invocaremos. A di- ferencia de CGLpm, que es una funcién estén- dar de todas las instalaciones de Perl, ésta debe venir de nosotros. ‘Como ya hemos sefialado, un médulo de Perl es muy parecido a otras formas de programa- cién orientada a objetos. Sin embargo, una de las principales diferencias es que los médulos deben estar en un archivo separado, mientras que en la mayoria de distribuciones de pro- gramaci6n orientada a objetos se trata sélo de una opcién. Existe otra diferencia importante: en la mayo- ria de sistemas de clases (incluido PHP), se han tenido en cuenta muchos de los detalles a la hora de establecer variables. No es este el caso de Perl, tal como veremos enseguida, Esta es la primera seccién de nuestro médulo imageMaster: #1 /usr/bin/perl use strict; use GD; package imageMaster; sub new { my ($eli my $self = { _source => undef, _size => undef, _target => undef, he bless $self, $class; return $self; } Se invoca el médulo GD porque lo utilizare- ‘mos a continuacién para editar la imagen. La cuarta linea define el nombre del paquete, que debe reflejarse en el nombre del archivo real, imageMaster pm. A continuacién, le sigue una subrutina (en terminologia de la'programacin orientada a objetos, un método, parecido a una funci6n). La linea de imager.pl, $edit = new imageMaster ;, en realidad es una llamada a esta subrutina. Su papel es crear una instancia de la clase imageMaster. Lo primero que haremos es declarar una clase y asignarle un nombre segtin el argumento en- viado a la subrutina. La variable especial @_ contiene cualquier argumento enviado a una subrutina, Puesto que hemos pedido new imageMaster, disponemos de una clase llamada imageMaster. 48 Editor de imagenes La siguiente linea contiene la creacién de una hash table (0 array asociativa). La hash table $se1£ contendré tres valores: $se1£- >source, $self->size y $sel£->target. Hemos reservado memoria para la hash table, pero todavia esta vacia. El nombre de variable se1£ se establece como convenci6n. Mientras, que imager.pl hace referencia a $edit, ima- geMaster.pm siempre utilizaré el nombre $se1£, sea cual sea el script que le invoca. La siguiente linea es crucial, puesto que con- tiene la creacién del objeto en s{ mismo. Para ello tomamos dos cosas: la hash table y la clase ~y ya podemos bendecirlas (en inglés, bless). Es poco probable que encontremos la funcién bless () fuera de Perl. En nuestro caso, su funcién es decirle al intérprete de Perl que to me $8e1¢ y lo convierta en un objeto de la cla- se especificada en $class, es decir un objeto imageMaster. Una vez hecho esto, devolvemos el objeto recién creado; en otras palabras, ce- rramos la subrutina devolviendo su producto, un $se1¢ totalmente preparado y... bendeci- do. A partir de ahora, ya podemos hacer referencia aa $seL£ enel resto del médulo, ya que esté definido como variable de clase. Cualquier llamada a él desde otro script, como $edit- >frame () ;, enviaré automaticamente $self como argumento. Podemos acceder a él desde el inicio de cada subrutina invocandolo de la siguiente forma: my $self = @_; Ahora que imager.pl ya ha llamado a la inicia~ lizaci6n de imageMaster, debe enviarle la in- formacién pertinente. Y lo hace, por ejemplo, indicando: $edit->source ($source) ; En realidad se trata de una llamada a una sub- rutina, porque por cada parte de informacién que el médulo puede recibir existe una subru- tina de asignacién. Tenemos tres allocators que pertenecen a imageMaster.pm: sub source { my ( $self, $source ) = @; $self->{ source} = $source if defined($source) ; return $self->{_source}; } sub size { my ( $self, $size) = @ + $self->{ size} = $size if defined($size); return $self->{ size}; } sub target { my ( $self, $target ) = @ 3 $self->{ target} = $target if defined ($target) ; return $self->{_target}; } En Ios tres casos, tenemos dos argumentos que se deben leer: la variable de clase $se1£ y la informaci6n que envia imager pl. Cuando hay dos o mas argumentos en @_, se convierte au- tométicamente en una matriz. asociativa (lash fable). En consecuencia, podemos declarar dos nombres de variable, uno por cada argumento que contiene. El prefijo my es muy importante en los médu- los Perl, ya que identifica una variable privada. Las variables privadas s6lo son accesibles des- de la subrutina en la que se declaran, y no pueden ser modificadas por ningtin proceso exterior. Eso significa que se cumple uno de los principios de la programacién orientada a ob- jetos, la encapsulacién. Dicho de otra forma, la ejecucién de nuestro médulo no interferira con ningtin médulo o proceso externo. Leyendo la segunda Iinea de nuestras subruti nas de derecha a izquierda, tenemos la funcién defined () que comprueba que haya infor- macién en el argumento enviado desde ima- ger.pl. De ser asi, el contenido se establece en la entrada relevante en la hash table de $se14. Esta informacién pasa a estar disponible para otras funciones dentro de imageMaster:pm llamando, por ejemplo, a $ae1£->source. Conversién de la imagen 49 Una vez creado el marco de trabajo y rellenado con informacién, pasaremos a la edicién de la imagen propiamente dicha. Hace rato que no Jo decimos pero... aunque no lo parezca, esta- mos creando un editor de imagenes. Cambio de tamaiio Supongamos que el usuario ha optado por modificar el tamaiio de la imagen. El XHR ha enviado, entre otros datos, las instrucciones method=resizeéwidth=400. El nombre de Ia imagen original se habré establecido en $se1£->source y el nuevo nombre generado aleatoriamente se encontrard en $sel£->target. Cuando imager pl com- pruebe ($method eq 'resize') lacondi- cién se cumplira, y se emitiran dos comandos. EL primero de ellos establece la anchura de la nueva imagen: $edit->size ($newwidth) ; El segundo llama a una nueva subrutina: S$edit->resample() Veamos el cédigo de la subrutina de cambio de tamajio: sub resample { my ($self) = @; my $newwidth=$self->size; my $source=$self->source; my S$target=$self->target; my $tmp= GD: : Image->newFromJpeg ($source) ; my ($width, $height) = $tmp->getBounds () ; my $newheight= sprintf ("%.0£", (Sheight/$width) *Snewwidth) ; ny $pic= GD: : Image->newTrueColor ($newwidth, §newheight) ; $pic->copyResampled ($tmp,0,0,0,0,$newwidth, S$newheight, $width, $height) ; open (FILE, ">../html/ajax/uploads/$target") ; print FILE $pic->jpeg; close FILE; print ""; } La primera Ifnea llama a $self ynosdaacce- 30 a toda la informacion que contiene. Las tres lineas siguientes toman la informacion de $self y la asignan a variables locales, Para respetar la encapsulacién, Perl no permite en- viar informaci6n a funciones externas a menos que se utilicen variables locales. Lincoln Stein El siguiente paso es empezar a utilizar la inter- faz de Perl con GD, el paquete de desarrollo grafico que nos permitird editar las imagenes. GD esté a disposici6n de los programadores de Perl a través de GD.pm, de Lincoln D. Stein. Como ya hemos avanzado su uso al principio del archivo imageMaster.pm, ya podemos crear una instancia del mismo. GD son las siglas de Graphics Draw, un paque- te escrito por Thomas Boutell. Originalmente, se escribi6 en C para trabajar sobre imagenes | IE, pero ahora est disponible para muchos | lenguajes y puede editat los formatos JPG, PNG y GIF. La implementacién de Perles de Lincoln Stein. Utilizando la variable local $tmp podemos hacer que todo vaya muy répido declarando nuestro objeto y envidndole la primera parte de la informacién. En consecuencia, asignamos a $tmp el estado de objeto GD::Image basan- donos en el resultado del envio de §source al | método newFrompeg(). La variable ¢tmp ha recibido la imagen cargada por el usuario, y a partir de ahora cualquier método aplicado a ‘$tmp se aplicard a la imagen original. 50 Editor de imagenes Torn Boutell El primer método al que debemos llamar es. getBounds (), que calcularé la anchura y la altura en pixeles de $tmp. Estos valores se en- vian a $width y $height. ‘Ya conocemos la anchura deseada de la ima- gen una vez. transformada, porque el usuario Ia ha especificado. Sin embargo, para poder determinar la altura correspondiente debemos realizar un célculo. Para mantener las propor- ciones de la imagen, podemos basarnos en el hecho de que la nueva altura se obtiene di diendo la altura original por la anchura origi- nal y multiplicando el resultado por la nueva anchura. De ahi deriva la formula: (Sheight/$width) *$newwidth. Sin embargo, el resultado de una formula co- ‘mo esta nos daria demasiados decimales, es algo inevitable cuando se dividen ntimeros re- ales, No resulta muy eficiente tener valores de punto flotante como este ocupando espacio en memoria durante todo el proceso. Por eso re~ curriremos a sprint (), la funci6n para mos- trar niimeros reales, para redondear el niimero al entero més cercano. Para que no devuelva decimales, especificaremos &. 0£. Ahora ya tenemos toda la informacién necesa- ria para realizar el cambio de tamafo. Para ello, crearemos una nueva instancia de GDz:Image con $pic. Esta vez no creamos una imagen a partir de un archivo jpg. En consecuencia, el método seré distinto: utili- zaremos newTrueColor () , el formato de tra- bajo estandar del entorno GD. Més tarde lo convertiremos en un jpg; La tinica informacion que debemos darle a GD en este punto es la al- tura y la anchura. El objeto $pic es una ima- gen vacia de las dimensiones correctas, y en la linea siguiente la rellenaremos. El método copyResampled() toma una ima- gen y cambia su tamaiio respetando el original Jo maximo posible. No debemos olvidar que las imagenes en formatos como JPG, PNG 0 GIF son de tipo raster, estén formadas por una rejilla de puntos luminosos de distinto color e intensidad. Cuando cambiamos el tamaiio de una rejilla debe ser forzosamente una aproxi- macién, y el resultado seré siempre ligeramen- te distinto al original. Si aplicamos una serie de cambios a una misma imagen y después la de- yolvemos a sus dimensiones originales, no sera tan nitida como la imagen original. De todos modos, el método de GD es igual de bueno que cualquier otro, por lo que no vale la pena buscar otras tecnologias. El método copyresampled () exige nueve argumentos. El primero es el nombre de la imagen original, los cuatro tiltimos son la an- chura y altura de la nueva imagen y la anchura y altura de la imagen original. Los argumentos segundo a cuarto son dos pares de coordena- das. En el supuesto de que necesitésemos mo- ver la imagen de una posici6n a otra, podemos sugerir una posicién original y una nueva po- sicién con dos pares de valores(x,y). En este caso, no necesitamos esta funcionalidad por lo que escribiremos (0,0). El objeto $pic ya tiene Ia version de la imagen original con el nuevo tamafio que ha solicitado el usuario. El siguiente paso consiste en guardar la ima- gen en un archivo. La operacién es casi idén ca al proceso de escribir un archivo de texto. Utilizaremos la funcién open () con el identi- ficador FILE y le daremos la direcci6n del ar- chivo a escribir, es decir $target, que contie- ne el nombre de archivo generado aleatoria- mente. Conversion de la imagen 51 El simbolo > indica que queremos escribir en el archivo. El comando print () aplica el méto- do “jpeg” de GD::Image a nuestro identifica- dor FILE, Este método se ocupa de decidir lo que se debe escribir, y produce la imagen en formato JPG. Existen métodos similares para crear imagenes GIF, GIF animado y PNG. Si prevés desarrollar una funcionalidad que permita al usuario elegir entre distintos tipos de formato de archivo, podrias utilizar estruc- turas de control en esta seccién para imple- mentar la seleccin del usuario, con los méto- dos ->Jpg, ->gif y ->png. Una vez cerrado el archivo, lo tinico que nos queda por hacer es producir una salida para el XHR, es decir un enlace a la nueva imagen. Marco La segunda opcién que le hemos propuesto al usuario le permite insertar un marco alrededor de la imagen. De momento no necesita mas in- formacién, aunque podrias ofrecer la opcién de elegir el color del marco si te interesase am- pliar el programa, Este método dibujaré un marco de cuatro pixe- les alrededor del borde exterior de la imagen cargada. Al dibujar sobre la imagen existente, as dimensiones no variaran , por lo que no se remuestrearé el contenido. La anchura del marco es otro elemento que podrfamos dejar en manos del usuario si quisiéramos ampliar la aplicaci6n, El marco utilizaré el color mas oscuro de la pa- leta que ofrezca la imagen. El noventa y nueve por ciento de las veces seré negro (casi todas Jas imagenes tienen como minimo un pixel que es negro o casi negro). Si la imagen se limita a azul y blanco, el marco tendré el color del azul més oscuro de la imagen. El motivo es que uti- lizamos la imagen actual con su paleta de colo- res, ya que cambiar la paleta de una imagen es un proceso mucho més complicado. Veamos la subrutina para crear el marco: sub frame { my ($self) = @; my $source=$self->source; ny S$target=$self->target; my $image = GD: : Image- >newFromJpeg ($source) ; my $black = $image->colorResolve (0, 0,0); my ($width, $height) = $image->getBounds () ; for (my $i=0; $i <= 3; $i++) { $image->rectangle ($i, $i, (Swidth- 1-$4), ($height-1-$i),$black) ;} open (FILE, ">... /htm1/ajax/uploads/$target") ; print FILE $image->jpeg: close FILE; print ""; Bs En las tes primeras lineas, recuperamos la in- formacién necesaria de nuestra variable de cla- se, igual que en la subrutina anterior, No nece- sitamos declarar dos objetos GD::Image en este caso, puesto que tomamos una imagen y dibu- jamos sobre la misma; no necesitamos enviar una imagen como argumento para crear otra. El objeto $image se declara con el original como $source El método colorResolve () nos proporcio- naré el color més oscuro de la paleta de la ima- gen si le damos los argumentos (0,0,0). Funcio- na de la siguiente forma: los colores de la pale- ta tienen valores comprendidos entre 0'y 255, para el componente rojo, el verde y el azul. Son los tres colores primarios para componer colo- res con luz, 52 Editor de imagenes La combinacién (255,255,255) corresponde al blanco, mientras que (0,0,0) es negro, es decir, Ia ausencia de color. La llamada al método colorResolve (0,0, 0) buscard el negro en Ja paleta de la imagen y, si no lo encuentra, de- terminara autométicamente el color mas cerca- no-es decir, el color més oscuro disponible. En 1a linea siguiente, utilizamos el mismo mé- todo que en la subrutina anterior para obtener las dimensiones de la imagen. Las necesitamos para dibujar correctamente el marco. Elbucle for es el que controla el dibujo del marco alrededor de la imagen. Como podemos ver, el bucle se ejecuta cuatro veces, con el va~ lor del contador $4 incrementando en cada ci- clo de 0 a3. Por cada ciclo del bucle, se dibuja- 14 un rectangulo en la imagen, partiendo del exterior y moviéndose un pixel cada vez. El método $image->rectangle recibe cinco argumentos: dos pares de coordenadas y un valor de color. Lee el primer par de coordena- das y las toma como punto de partida de un rectdngulo; el segundo par de coordenadas describe el punto opuesto del rectangulo. Ba- sindose en esta informacién, dibuja un rectin- gulo con lineas de un pixel, con el color especi- ficado en $black. La primera vez que se ejecuta el bucle, traza un rectangulo que parte de (0,0) en los limites menos uno. De este modo, el recténgulo estaré alrededor del interior del lienzo. La segunda vez que se ejecuta el bucle, dibuja un recténgu- Jo un pixel dentro del anterior, y asi hasta que se ha dibujado el borde de cuatro pixeles. Una vez hecho esto, se escribe el archivo, igual que en el ejemplo anterior, y el enlace al mis- mo se devuelve al XHR. Ajax a punto de degollar un carnero. No olvidemos el 1; La subrutina final del médulo Perl es la que utiliza imagen.pl para eliminar la versi6n ori- ginal del a imagen. Este es el resto de cédigo de imageMaster p|: sub delete { ny ($self) = @; my §victim = $self->source; unlink ($victim) ; } qs Quizas te preguntas qué significa ese ntimero uno al final del médulo, La respuesta es senci- lla: la ejecucisn de un médulo Perl se puede considerar un evento booleano. Esto nos per- mitirfa un uso como el siguiente: $go=if {new dodgyModule} else {die("1")} De este modo, podemos gestionar mejor los errores. En el ejemplo anterior el programador mantiene el control del flujo de informacién en el caso de que la llamada al médulo falle, pro- vocando explicitamente el envio de un mensaje antes de detenerse. Conversién de la imagen 53 Sin embargo, para que la ejecucién de un m6- dulo devuelva el valor true, es necesario termi- narlo con una instruccién true. Y la que utili- zan més conuinmente los programadores de Perl es el niimero 1. Implementacién PHP Tras este estudio detallado de la creacién del médulo Perl, la version PHP de la misma apli- caci6n seré un juego de nifios, Este es el archivo imager php: $source, ‘size'=> $size, ‘target'=> $target); $go=new imageMaster (elements: if ($method=="resize") { $go->resize(); } if ($method=="frame") { $go->frame(); } $go->delete(); 2> La primera Ifnea indica la ruta del archivo que contiene nuestra clase imageMaster. Utilizaremos una matriz asociativa en nuestra clase PHP, igual que en Perl hemos usado una hash table. De todos modos, en este caso ya preparamos la array en el primer script PHP, y la enviamos a imageMaster con la informacion recibida de uploader.php. Esta es la primera parte del archivo imageMas- ter.php: source= $elements['source'] ; $this->size=$elements['size']; $this->target= $elements[‘target']; i Enseguida esta hecho: la declaracién de la cla- sey los constructores, en s6lo nueve lineas. En la declaracién de las variables utilizamos el tipo private, que garantiza el mismo tipo de encapsulacién que ofrecia my en Perl; ningtin otro proceso podré acceder a las variables que se utilizan en este objeto. Una vez declaradas las variables que recibirén la informacién de imager-pl, utilizamos la funcién __sonstruct () para crear una matriz asocia- tiva, a fin de reunirlas en una variable de clase. Esta funcién es una funcionalidad estandar de Jas clases PHP: siempre se ejecuta la primera vez. que se invoca una clase. La informacion de la array estaré disponible para todas las demas funciones dentro de la clase, como $this->source, §this->sizey $this->target, El tipo de datos private sdlo esta disponible a partir de PHP 5. Sino puedes instalar la ver- sion 5 0 si tu ISP no la oftece, deberds utilizar De forma parecida, la funcion estandar __senstruct () no existe en la version 4. En su lugar, la funci6n deberia tener el nombre de Ia clase, en este ejemplo, imageMaster (). 54 Editor de imagenes Ya tenemos nuestra variable de clase repleta de informacién, por lo que podemos pasar a los métodos del objeto que realizarén las modifi- caciones en la imagen. Cambio de tamafio Este es el primer método: dze() { $src=imagecreatefromjpeg ($this->source) ; list ($width, $height) = getimagesize ($this->source) ; Snewwidth=$this->size; function x $newheight=round ((Sheight/$width) *$newwidath) ; //redondear para evitar 11 //decimales $tmp=imagecreatetruecolor ($newwidth, $newheight) ; imagecopyresampled ($tmp, $src, 0,0,0,0,$newwidth, §newheight, $width, Sheight) ; imagejpeg($tmp, "uploads /$this->target",100) ; imagedestroy ($src) imagedestroy ($tmp) + echo "target>"; } En esta situacién, el usuario ha seleccionado la funcién de cambio de tamaiio y ha especifica- do una anchura para la nueva versi6n de la imagen. A diferencia de Perl, no es necesario invocar explicitamente el paquete GD, pode- mos empezar a usar los métodos de GD. La imagen original se abre y se guarda en $arc llamando al método imagecreatefrom}peg (). La funcién List () nos permite recuperar la anchura y la altura que devuelve getimagesize() y asig- narlas a nuestras dos variables $width y Sheight. Como ya hemos explicado en el método de cambio de tamafo de Perl, podemos calcular la nueva altura tomando la nueva anchura que ha especificado el usuario y se ha guardado en $this->size, multiplicarla por la altura ori- ginal y dividirla por la anchura original. Ya podemos crear una nueva imagen con las di- mensiones correctas, con imagecreatetruecolor (). El objeto $tmp es una imagen vacfa, preparada para recibir el original redimensionado. La funcién imagecopyresampled () se en- carga del cambio de tamaiio propiamente di- cho. Necesita 10 argumentos. Los dos primeros son la imagen nueva y original, respectivamen- te. Los cuatro siguientes son dos pares de co- ordenadas que permiten al programador co- piar un area de una imagen en otra. No es nuestro caso, por lo que dejaremos los valores (0,0). Los cuatro tiltimos argumentos son, por orden, la nueva anchura y la nueva altura, y la anchura y la altura originales. Una vez se ha ejecutado imagecopyresampled(), la nueva imagen ya existe en memoria, redimensionada ala anchura especificada por el usuario. Ahora tenemos que escribir la imagen en un archivo. El método image; peg () recibe tres argumen- tos: un objeto GD, una ubicacién de destino y un nivel de compresién JPG. Especificaremos Ia imagen redimensionada $tmp, el destino re- cibido de imager.php y el valor 100. El proceso de compresi6n JPG implica pérdida, lo que sig- nifica que siempre se pierde calidad de ima- gen. Si el espacio que ocupan los archivos es prioritario, la compresién JPG puede reducir el tamaiio del archivo reduciendo la calidad de imagen. Con un nivel de compresién de 10 la imagen seria horrible, y con 60 estarfa ligera- mente degradada. Conversién de la imagen 55 Puesto que buscamos obtener un redimensio- namiento de la maxima calidad posible, evita- remos los efectos de la compresién especifi- cando un valor de 100. El archivo ya esté listo y practicamente hemos terminado. ‘Todavia nos queda un proceso indispensable, que no aparecia en Perl, El espacio reservado en memoria por los procesos GD para nuestras dos imagenes no quedaré liberado hasta que no lo pidamos, usando el método imagedestroy(). Por ese motivo ejecutamos este método una vez por cada una de las imagenes, $src y $tmp. Perl dispone de un sistema de gestion de la memoria que realiza esta tarea autométi- camente. Para terminar, el enlace a la nueva imagen se devuelve al XHR. Es sencillamente una cadena que contiene el directorio de carga y el nombre generado aleatoriamente por uploader.php. Marco La funcién del marco creara un borde de cua- tro pixeles alrededor de los margenes exterio- res de la imagen. Como ya hemos visto antes, esto nos permitiré aplicar un marco a la imagen sin cambiar sus dimensiones ni reducir su calidad. El color del marco seré negro o el color més proximo al negro que se encuentre en la paleta de colores de la imagen. Igual que en el método de cambio de tamaio, ‘empezaremos abriendo la imagen original y asignéndole el objeto $sxc con ayuda de imagecreatefromjpeg(). En esta ocasién, no es necesario crear una se gunda imagen en GD, porque nos limitaremos a dibujar sobre la imagen original. Este es el método: function frame() { $src=imagecreatefromjpeg ($this->source) ; list ($width, $height) = getimagesize($thi $black= imagecolorresolve ($src,0,0,0) ; for ($i=0; $ic=3; $i++) { imagerectangle($sre, $i, $i, $width-1-$i, $height-1-$i, $black) ; } imagejpeg($src, “uploads/$this->target", 100); imagedestroy (src) ; echo "target>"; >source) ; } Recuperamos las dimensiones de la imagen original pidiendo a List () que envie el resul- tado de getimagesize() a $widthy $height. La tiltima informacién que necesi- tamos es el color més oscuro de la paleta de nuestra imagen. El método imagecolorresolve () busca el negro (0,0,0) o el color mas cercano posible y lo asigna a nuestra matriz, $b1ack. (Para mas in- formacién, consulta la seccién sobre Perl). A continuacién, invocamos un bucle for para dibujar los cuatro recténgulos que constituiran el marco de cuatro pixeles. El método imagerectangie () recibe 6 ar- gumentos: la imagen en la que se dibujard el rectangulo, las coordenadas de la esquina su- perior izquierda del recténgulo, las de la es- quina inferior derecha, y un color. Cuando el bucle se ejecuta por primera vez dibuja un rec- téngulo desde (0,0) hasta el punto mas alejado; Ja segunda vez dibuja un recténgulo un pixel dentro del primero, y asf hasta haber trazado cuatro. 56 Editor de imagenes Para terminar el método, escribimos el archivo, eliminamos el objeto GD de la memoria y pre- paramos el enlace a la nueva imagen, recién enmarcada. El tiltimo comando de imager.php es una lla- mada para climinar la imagen original del ser- vidor. Esta es la tiltima parte de nuestro archi- vo imageMaster.php: function delete() { unlink ($this->source) ; } } 2> Como ya hemos visto anteriormente, serfa po- sible retrasar la eliminacién, permitiendo asi que el usuario “deshaga” una o varias de las. funciones que haya aplicado a la imagen. Una posible forma de hacerlo seria almacenar enel elemento edit form de uploader.php no s6lo los nombres del archivo original y nuevo, sino también el nombre de archivo anterior. Una funcién JavaScript, dentro de uploa- der.php, podria memorizar el nombre de ar- chivo anterior llamando al nombre de archivo original cada vez que se llama al XHR. De este modo, imager. php podria eliminar el archivo anterior cada vez (si existe) en lugar de borrar el original. Esto permitiria un solo pro- ceso de “deshacer”. Dejo en tus manos la brisqueda de una solu- cién si quisiéramos deshacer varias acciones, pero recuerda que debes encontrar una forma de borrar las imagenes obsoletas. Fin del juego: jprohibido marearse! Practicamente hemos llegado al final de esta parte de nuestra aplicacion de edicién de imé- genes, pero todavia no esta del todo termina- da. Reinicializacién de la aplicacién La informacién devuelta al XHR se insertaré en la pagina de la forma habitual, pero todavia queda algo. Para que el programa de edicién permita al usuario realizar tantos cambios en Ia imagen como desee, debemos asegurarnos de que la aplicacién contintie indefinidamente. Veamos la funcién updatepage () de uploa- der.htm y uploader.php: function updatepage (str) { document . getElementById ("result") -innerBTML = str; var form = document . forms ['imageform'] ; var upper = form. filename.value; form.location.value = “uploads/" + upper; randomString(); } La primera linea muestra el enlace al archivo recién creado en el elemento
. Puesto que este elemento conte- nia la imagen original y ahora recibe el enlace a la nueva imagen, el efecto que se obtiene es la sustituci6n de la imagen antigua por la nue- va. La siguiente linea busca nuestro elemento for- mulario y le atribuye la variable form. A continuaci6n, reinicializamos la aplicaci6n; se trata sencillamente de eliminar lo antiguo y preparar lo nuevo. Tu turno 87 Elnombre de la imagen original se sustituye por lo que, hasta ahora, se ha considerado co- mo el nuevo nombre de archivo: form. filename. value se copia en form. location.value. A continuacién, llamamos a la funcién randomstring() para crear un nuevo nombre de archivo aleatorio e insertarlo en form. filename. value. Veamos rapidamente c6mo funcionara. La primera vez que el usuario vea la pagina de carga verd su propia imagen, que conservaré su nombre original, imaginemos que casade- madera.jpg. La accién onLoad de ha llamado a randomstring (), que ha creado el nombre de archivo gyah5fhd.jpg. La clase ima- geMaster leerd el archivo casademadera,jpg y escribir gyahSthd jpg. Una vez que el nuevo archivo se muestre en la pagina Web, gyahSthd,jpg se guardaré como nombre de imagen original en form.1ocation.value y 1500gaak,jpg, generado por una nueva llamada a randomString(), se asignard a form. filename.value. Cuando termine la siguiente funcién de edicién, r500gaak jpg, sera Ja imagen original y se crear un nuevo nom- bre de archivo aleatorio. Y asi indefinidamen- te, Definir el estilo El tiltimo elemento de nuestro archivo uploa- der es una llamada a una hoja de estilos sepa- rada, a la que imageperl.html y imagephp.html también hacen referencia. Esta linea se encuen- tra en la seccién de cada uno de los archivos: Y este es el archivo imagemaster.css: body, p, input, textarea, div, span { font: 10pt verdana, arial, sans-serif; } En este momento es una declaracién minima- lista; un tipo de fuente para todos los contene- dores HTML de la lista. Una vez que convier- tas la aplicaci6n en un producto mas completo, sin duda deberds perfeccionar la hoja de esti- los. Tu turno Una vez que la aplicacién ya esté funcionando, tendras la base de lo que podria convertirse en un sitio Web de edicién de imagenes muy titil. Existen varios puntos en las secciones anterio- res en las que hemos mencionado posibles ca- minos para ampliar la aplicaci6n, y otras que puedes inventarte por tu cuenta. Estas son s6lo algunas de las formas posibles de desarrollar este proyecto: © Ampliar la gama de formatos de imagen de entrada y salida para incluir GIF y PNG. © Permitir al usuario “deshacer” cambios en una imagen, volviendo a una o varias ver- siones anteriores de la misma. + Permitir que el usuario seleccione un nom- bre de archivo y una ubicacién de destino para la imagen modificada. + Permitir que el usuario elija una imagen de una selecci6n y la edite en el momento (0 incluso varias a la vez). * Ajiadir mas funciones de edicién de la imagen como rotacién, desaturacién (blan- co y negro), recorte, afiadir marcas de agua, copyright y muchas otras de las ta~ reas que permite el paquete GD. * Mejorar las funciones existentes, por ejem- plo permitiendo que el usuario elija un co- lor o un grosor para el marco. ‘* Agregar la posibilidad de combinar varias imagenes en un GIF animado. Existen muchas otras posibilidades para am- pliar la aplicaci6n y convertirla en una herra- mienta verdaderamente titi El hecho de afiadir funcionalidad suele signifi- car la adicién de otro método a la clase. Existen dos formas igualmente vélidas de hacerlo. La primera es empezando por la parte HTML, fiadir otro botén de opcién al formulario que inicialmente no haga nada, y probarlo en el navegador. 58 Editor de imagenes A continuacién, deberfamos afiadir la informa- cién adicional relevante en la funcién getquerystring() del XHR, y probar el c6- digo JavaScript y la cadena POST que se envia al servidor. Finalmente, comprobariamos que la informa- cién llegue a la aplicacién del lado del servidor y afiadiriamos un método que inicialmente s6- Jo mostraré un mensaje de depuracién del tipo “Hola, acabas de optar por cambiar el tamaito de la imagen”. Cuando el nuevo método funcione, la nueva funcionalidad estar4 completa y lista para pro- barla. La segunda opcién consistirfa en empezar por escribir el método, y después ajustar el XHR para que llame cada vez.a este método. A continuaci6n, modificarfamos el formulario para que llame automaticamente al nuevo mé- todo y, para terminar, modificarfamos el for- mulario para que sélo lame al método si el usuario lo ha seleccionado. A priori, los dos métodos son igualmente vali- dos, pero hay un buen motivo para utilizar el segundo. Se empieza por la parte dificil de la programaci6n, por lo que cuando el cansancio y la impaciencia empiecen a afectar tu rendi- miento, slo te quedaré una tarea (relativa- mente) sencilla por hacer. Tyler Mane en el papel de Ajax, en Ia pelicula Troya (2004). En el momento de la acci6n, Ajax es- 44 furioso contra el capital del barco. Si amplias este proyecto o simplemente ests contento de haber logrado que funcione, en- vianos el enlace de tu editor de imagenes ter minado a info@pc-cuadernos.com para poder presentar ejemplos de cémo AJAX puede en- cargarse de la edicién grafica. Conversor XML. 59 Conversor XML En el siguiente ejemplo combinaremos dos de as tecnologias que la mayoria de expertos con- sideran como lo mas “puntero” en desarrollo para Internet: AJAX y XML. 2A qué viene tanto revuelo con XML? Para los no iniciados, resulta dificil comprender qué tiene de especial. ;Cémo pueden un montén de tags en un sencillo archivo de texto ser tan importantes en un mundo de tecnologias in- crefblemente complejas como las biisquedas, las selecciones répidas o la privacidad de la red? El ideal XML Algunos de los atributos de XML lo convierten en una tecnologia especialmente interesante. Es un lenguaje muy sencillo. Hay pocos len- guajes informaticos que se puedan considerar “legibles por un ser humano”, pero sin duda XML es uno de ellos. Veamos un ejemplo de un archivo XML sencillo: 1.0" encoding="UTF-8"?> stitulo>La Regenta 1884 Fortunata y Jacinta 1886 1980 El Quijote 1605 Si se compara con las expresiones regulares de Perl, este tipo de informacion es mucho més accesible. La idea bésica es evidente a primera vista: tenemos una categoria contenedora glo- bal, una agrupacién de tres objetos categoriza- dos como “novelas”, cada una con sus caracte- risticas. La categoria que las contiene a todas es el elemento rafz. Cada novela es un nodo, y los elementos que contiene cada nodo se conocen como propiedades. La lista de propiedades de cada nodo no siem- pre es idéntica. El segundo elemento tiene un elemento “teleserie”, que indica que se cred una serie para televisién basada en el libro For- tunata y Jacinta en 1980. El primer nodo es el encabezado, y establece la codificacién en “utf-8” para que se puedan uti- lizar sin problemas acentos y otros caracteres especificos del idioma. El guién bajo se utiliza porque los nombres de los nodes no deberian contener espacios. Ademés, hay una serie de caracteres especiales que no se pueden utilizar en las etiquetas de nodos XML o dentro de sus propiedades, como &, ', ", . En su lugar deben usarse los equivalentes HTML: & , fapos;, ", elt; y egt: Es facil identificar r4pidamente una gran ven- taja de XML con respecto a HTML: la posibili- dad de crear nuestras propias etiquetas. Este ejemplo es un archivo XML sencillo de tres veles, pero se pueden utilizar muchos més ni: veles y propiedades, lo que permitiria almace- nar grandes cantidades de informacién en una estructura jerarquica disefiada por nosotros mismos. Este es uno de los motivos por los que los creadores de OpenOffice han optado por usar el formato XML como formato de archivo. Tanto si se trata de archivos de texto como de hojas de célculo o diapositivas, todos sus for- matos son conjuntos comprimidos de archivos XML. 60 Conversor XML. Elhecho de que OpenOffice recurra a este sis- tema para muchos tipos de archivo distintos subraya otra de las cualidades de XML, la por- tabilidad. Los datos guardados en XML se pueden transportar, traducir o convertir fécil- mente a otros formatos, gracias a su estructura sencilla. Incluso Microsoft se ha convencido de adoptarlo aunque, fieles a su tradicién, lo han hecho de una forma “propietaria” y en lugar de adoptar las definiciones XML esténdar crea- ron su propia version, MS XML. En este ejemplo final llevaremos la portabili- dad a extremos insospechados. Tomaremos un bloque XML sencillo de tres niveles, como el del ejemplo anterior, y lo convertiremos a va- rios formatos, desde hojas de calculo hasta ar- chivos de sonido. Sin embargo, aunque los archivos XML sean sencillos en si mismos, procesar la informacion que contienen puede resultar complicado. Para procesar la informaci6n almacenada en nues- tro archivo XML debemos convertirlo en algo que PHP y Perl puedan comprender, lo que significa arrays asociativas y hash-tables. Por suerte, existen herramientas para convertir XML en estos formatos, que nos ahorraran el trabajo de parsear los datos. De todos modos, la manipulacién de las matrices y hash tables resultantes puede convertirse en todo un reto, como veremos enseguida. Objetivo de la aplicacién Para aprovechar las cualidades mas importan- tes de XML y AJAX, nos proponemos crear una aplicacién Web que permita al usuario in- troducir un cédigo XML de tres niveles en un formulario y elegir uno de seis formatos de sa- lida posibles. Por cada formato seleccionado, se crearé un nuevo archivo que contendré la informacién del XML del usuario. Esta es la lis- ta de formatos que estarén disponibles en la aplicacién inicial: XML: por defecto, la informacién proporcio- nada por el usuario se escribiré en un archivo XML. En funci6n del navegador, el archivo podra ser legible o no, pero sera XML puro, es decir, que a menos que tenga asociado un ar- chivo CSS 0 XSL, no se mostrar correctamente (aunque por lo general, si se mostrara). Una extensién titil de esta aplicaci6n seria incluir la generacion de un archivo CSS referenciado por la salida XML. Sin embargo, Internet Explo- rer 5 no puede leer XSL, por lo hay que tener en cuenta qué navegadores se usarén. Texto: archivo de texto sencillo con extensién “ txt”. Este formato puede ser lefdo por todos los editores de texto conocidos en cualquier sistema operativo, con la tinica limitacién de que no permite guardar informacién de forma- to exceptuando saltos de linea, espacios y tabu- laciones. Se podria prever ampliar esta posibi- lidad convirtiendo el contenido a formato RTF de Microsoft, capaz. de guardar tanta informa- cin de estilos como los documentos de Word, y compatible précticamente con cualquier pro- cesador de textos. PDF: el formato Portable Document Format, creado por Adobe para guardar documentos en un formato grafico. Las versiones modernas de Macintosh, Windows y la mayor parte de distribuciones de Linux tienen instalado Acto- bat Reader o un programa equivalente para leer PDF. Esto, y el hecho de que fuese disefia- do para mostrar texto correctamente (a dife- rencia de JPG), lo convierten en un buen can- didato para la salida XML. El método emplea- do aqui sera convertir el texto del archivo txt a PostScript, un formato parecido a PDE pero menos universal, con ayuda del programa a2ps. El archivo PostScript se convertiré poste- riormente a PDF con ps2pdf. Estos dos pro- gramas estn disponibles para Linux, Win- dows y Macintosh. Conversor XML 61 Wav: para permitir el acceso al contenido a personas con discapacidad visual, o para la posibilitar la creacién de anuncios puiblicos 0 mensajes telefénicos, y también por pura di- versi6n, podemos convertir el contenido XML en un archivo de audio. El formato WAV pre- senta varias ventajas. Précticamente cualquier equipo puede reproducir archivos WAV, ya que es uno de los formatos mas antiguos; mu- chos navegadores pueden reproducirlos sin necesidad de plug-in. Ademés, es mucho més répido de generar que los archivos MP3, aun- que el archivo resultante suele ser mucho ma- yor. En este ejemplo usaremos el programa LAME para convertir el archivo “txt” en un archivo de audio. La conversi6n puede imple- mentarse como enlace hipertextual, pero tam- bién se puede incrustar, de modo que una vez, que la conversi6n esté hecha el usuario pueda escuchar el resultado directamente. LAME también esté disponible para Windows HTML: los datos guardados en nuestro cédigo XML se presentarén en una tabla sencilla, con una fila de encabezado para los nombres de los elementos. El contenido serd compatible con WAP, de modo que se podria mostrar en un PDA 0 teléfono mévil. CSV: el formato CSV (valores separados por comas, Comma Separated Values) se utiliza para guardar informacién en archivos de texto de forma que los programas de hojas de célculo puedan acceder a los datos por filas y colum- nas. Escribiendo el contenido de cada nodo en una Ifnea, separada por comas, poclemos crear un archivo CSV vélido. En Internet Explorer, esto es lo que ocurre cuando se hace clic en el hiperenlace, siempre que el usuario tenga ins- talado un programa de hoja de céleulo: "ap wercoveats ones rye castes tc 1D ecesnn mentiorenapesor encom on eters notin El archivo resultante se podré abrir en forma de hoja de célculo convencional. Los posibles usos de esta aplicacion son miilti- ples. Imaginemos, por ejemplo, que el respon- sable de una base de datos de contactos quisie- ra poner la informacién sobre los miembros de la empresa disponible en distintos formatos. Podrian crear un formulario que recibiese in- formacién sobre cada contacto, formatearla en XML y almacenarla en una base de datos. Un segundo formulario separado permitiria a to- dos los empleados de la empresa recuperar la informaci6n e imprimir la informacin de con- tacto a partir de texto, mostrarla como PDE, abrirla en Excel o dejarla como mensaje de telé- fono. Y todo ello en un instante, gracias a las acciones répidas y discretas de AJAX. Plan de ataque Seria posible crear parsers de XML en PHP y Perl pero, como ya hemos visto, no necesita~ mos hacerlo porque existen funciones esténdar en PHP y un médulo de Perl que pueden con- vertir XML en tipos de datos utilizables. Esto nos ahorraré muchos problemas. 62 Conversor XML En el caso de PHP utilizaremos indirectamente las funciones esténdar de XML Parser. El sitio ‘Web ynww.php.netmanual/ incluye descripciones detalladas y ejemplos de todas las funciones. estandar de PHP, con un espacio para las con- tribuciones de los usuarios. De este modo, los, programadores pueden compartir y evaluar sus implementaciones de las funciones, crean- do una comunidad de e6digo abierto con un gran potencial. Una de estas implementaciones utiliza el XML Parser para convertir un bloque de XML en una matriz asociativa. Fue desarro- lado por “forquan” y modificado por Jan Vavtivek . Utilizaremos este cédigo, que se pu- blicé como contribucién de usuario el 18 de di- ciembre 2006 a las 8h53 en la pagina hittp://ch2. php.net/xml. También existe una suite de funciones de PHP llamadas SimpleXML, pero solamente estén disponibles para PHP 5. Su uso imposibilitarfa el uso 0 la modificacién de los ejemplos de este cuaderno a los usuarios de PHP 4. El plan de ataque de Ajax era sencillo: a golpes! En Perl utilizaremos el médulo XML:Simple, que se puede descargar bajo Linux con la ayu- da del comando epan: cpan> install XML::Simple En ActivePerl para Windows, el comando es el siguiente: ppm install XML-Simple Los datos devueltos por las funciones PHP y el médulo Perl estan muy relacionados, pero no son exactamente idénticos. También se usan formas ligeramente distintas para extraer los datos, aunque es posible reconocer patrones claros. Las diferencias no serén perceptibles para el usuario final, excepto porque el resul- tado final diferira en un pequefio detalle. Lo veremos mas adelante. El plan es el siguiente: proporcionar al usuario un formulario HTML que contenga un elemen- to
Formato textocbr /> Formato gréficocbr /> Formato audio
Formato Web
Hoja de cAlculocbr /> El primer elemento del formulario es un area de texto, que en este caso tiene un tamaiio bas- tante grande, En funcién del uso que se le dé puede ser necesario hacerla atin mas grande. Los siguientes elementos son casillas de verifi- cacién, cada una con una etiqueta sencilla, vin- culadas a un formato de archivo. El botén de envio tiene el valor “Convertir”, lo que cambia el texto que aparece en el propio bot6n. El evento onClick provoca la accién del XHR. Elarchivo xmlperl.html es précticamente idén- tico, a excepci6n del argumento que se envia al XHR, que pasa a ser "/ogi-bin/conversion.p1" El texto del principio de la seccién so- licita al usuario que inserte “cédigo XML sen- cillo de tres niveles”. En esta version, nuestra aplicacién no procesaré archivos XML con mas de tres niveles, es decir un elemento raiz. con un determinado mimero de nodos, cada uno de los cuales contendra una serie de propieda- des. Seria posible excluir los nodos 0 las pro- piedades, pero no aportaria demasiado desde el punto de vista del almacenamiento de in- formacién. También seria posible, si fuese necesario, am- pliar la profundidad del cédigo XML, pero pa- ra un uso esténdar un bloque XML de tres ni- veles seré suficiente. Cuando solicitamos cédigo al usuario, siempre es aconsejable proporcionarle un ejemplo con el formato correcto. A diferencia de HTML, XML no es un formato que lo perdone todo. Si en un cédigo HTML falta una etiqueta de cie- rre pueden darse resultados impredecibles, pe- 10 el navegador intentard mostrar el contenido que sigue a la etiqueta de abertura y continua- rd analizando el resto del archivo. En XML, este tipo de aberraciones son inacep- tables. Los archivos que no se ajusten a las normas serén rechazados. Para garantizar que el usuario pueda ver como minimo un ejemplo funcional, le ofreceremos un cédigo XML a modo de orientacién. Este cédigo se encuentra en el resto de la secci6n de xmiphp.html: 64 Conversor XML.





cbr />cbr />

Ejemplo:
10> Este cédigo sugiere una pregunta: zpor qué co- locar el texto de ejemplo en un Area de texto? En este caso el uso de un elemento