Certificación Zend PHP 5.3 (V) – XML con SAX, DOM y SimpleXML (II)

Tras unas semanas de inactividad, retomamos los posts sobre la certificación Zend PHP5 y en esta ocasión vamos a continuar con el tema del tratamiento de XML.

Es bastante probable que nunca hayáis usado SAX, la tecnología que vimos en el anterior post pero si en alguna ocasión habéis usado webservices a plataformas remotas, implementado métodos de pago y este tipo de interfases seguramente habréis usado DOM o SimpleXML.

Ambas tecnologías cargan el XML completo en memoria y en las últimas versiones de PHP podemos alterar el XML añadiendo o eliminando nodos y atributos del mismo antes de guardar el documento. Recordemos que en SAX esto no era así, PHP iba parseando el documento, saltaban eventos y en ningún caso podíamos alterar el contenido del archivo XML.

DOM es una recomendación del W3C y si hemos programado en Java u otros lenguajes las funciones nos sonarán un montón. El problema de DOM es que es muy verbose, bastante complejo y algo pesado de usar. En cambio SimpleXML es exclusivo de PHP pero cuando lo usas te preguntas por qué el W3C se complicó tanto y no hizo algo como esto.

En cuanto a la certificación, es bastante habitual que nos pregunten alguna cosita de ambas tecnologías así que vamos a repasar las funciones más utilizadas con algunos ejemplos y al final como siempre os pondré algunas preguntas y respuestas del examen.

DOM

La API DOM es muy extensa y llevaría mucho tiempo detallar todas sus funciones. Podéis revisar la versión completa en PHP.net y como veréis soporta cosas como:

– Validaciones con DTD
– Validaciones con Schemas XSD
– Validaciones con Relax NG (que es una sintaxis de XSD algo más simple)
– Consultas XPath sobre los documentos (alguna vez cae algo de esto en la certificación!!!)
– Todo tipo de funciones para editar el documento XML con su árbol de padres – hijos – siblings, etc…

Probablemente las funciones más utilizadas sean:

CARGA DE DOCUMENTOS

– Instanciar DOM: $mixml = new DOMDocument(‘1.0’, ‘UTF-8’);
– Cargar archivo físico $mixml->load($path_archivo).
– Cargar string $mixml->loadXML($stringXML);

LECTURA DE NODOS

– Recuperar los tags con un nombre $elemento->getElementsByTagName(‘nom_tag’)
– Cada uno de estos elementos nos llega como ->item(0), ->item(1)
– Podemos iterar los resultados con un bucle foreach
– Valores de atributos con $nodo->getAttribute($nom_atributo)

EDITAR NODOS

– Podemos acceder a los nodeValues y atributos y sobreescribirlos
– Para crear nodo $nuevonodo = new DOMElement($mitag)
– Para crear nodo de texto $nuevonodo->createTextNode($contenido)
– Para añadir nuevos nodos $nodopadre->appendChild($nuevonodo).

BORRAR NODO

– Borrado de nodo usando su parent: $nodo->parentNode->removeChild($nodo);

GUARDAR XML

– Guardado en disco: $documento->save($rutadestino);
– Guardado a string: $stringXML = $documento->saveXML();

A continuación, un par de ejemplos PHP sobre el documento de la tabla periódica de The XML Bible que podéis descargar aquí

Ejemplo parseo DOM que cuenta los nodos de tipo ATOM y lista la tabla

<?php
   $elementos = new DOMDocument();
   $elementos->load("allelements.xml");         
   $atoms = $elementos->getElementsByTagName("ATOM");
 
   echo "<p>Hay ". $atoms->length . " átomos</p>";
   foreach($atoms as $miatom)
   {
       $nom = $miatom->getElementsByTagName("NAME")->item(0)->nodeValue;
       $numatomic = $miatom->getElementsByTagName("ATOMIC_NUMBER")->item(0)->nodeValue;
       echo "<p>NUM ATOM: " . $numatomic . " , ELEMENTO: " . $nom . "</p>";
   }
?>

Creación al vuelo y envío al navegador con headers XML

<?php
   $nuevoxml = new DOMDocument("1.0", "UTF-8");
 
   $raiz = $nuevoxml->appendChild(new DOMElement("root"));
   $taginterno = $raiz->appendChild(new DOMElement("interno"));
   $text = $taginterno->appendChild(new DOMText("hola hóla"));
 
   $string = $nuevoxml->saveXML();
   header("Content-Type: text/xml");
   echo $string;
?>

SimpleXML

Como hemos visto, DOM es bastante farragoso a la hora de trabajar con él. Es súper potente pero la inmensa mayoría de las veces solamente necesitamos parsear y como mucho alterar algún nodo / atributo. Además, normalmente los XML son lo suficientemente pequeños para caber en memoria, por tanto raro es que necesitemos SAX.

SimpleXML, como he comentado antes, es exclusivo de PHP5 y como su nombre indica pretende simplificar el proceso de creación, lectura y actualización de los archivos XML.

Para cargar un documento tenemos las siguientes opciones:

– simplexml_load_file($fichero_fisico)
– simplexml_load_string($xmlstring)

Para acceder a los nodos, usa una sintaxis tipo array y propiedades públicas de un objeto, que si lo pensamos es muchísimo más cómodo que las funciones de DOM. Por ejemplo, para recuperar el nombre del átomo 23 es tan fácil como: $nomatom23 = $xml->ATOM[23]->NAME;

Un tema algo extraño es que SimpleXML siempre espera almenos 1 elemento. Si estamos creando un documento
desde 0 deberemos inicializarlo con DOM o con un String.

Además, podemos añadir elementos y atributos de forma sencilla en cualquier punto con addChild($nodo, $contenido) o addAttribute($nombre, $valor).

Repetimos los ejercicios anteriores, pero usando SimpleXML

Ejemplo parseo SimpleXML que cuenta los nodos de tipo ATOM y lista la tabla

<?php
$elementos = simplexml_load_file("allelements.xml");
 
echo "<p>Hay ". count($elementos->ATOM) . " átomos</p>";
foreach($elementos->ATOM as $miatom)
{
       $nom = $miatom->NAME;
       $numatomic = $miatom->ATOMIC_NUMBER;
       echo "<p>NUM ATOM: " . $numatomic . " , ELEMENTO: " . $nom . "</p>";
}
 
?>

Creación al vuelo y envío al navegador con headers XML

<?php
   /* Siempre necesita almenos 1 elemento, podemos usar DOM */
   $header = new DOMDocument("1.0", "UTF-8");
   $raiz = $header->appendChild(new DOMElement("root"));   
   $nuevoxml = simplexml_import_dom($header);
   /* O podemos usar un string directamente */   
   $nuevoxml = simplexml_load_string("<?xml version='1.0' encoding='UTF-8'?><root></root>");
 
 
   $taginterno = $nuevoxml->addChild("interno","hóla hola");   
   header("Content-Type: text/xml");
   echo $nuevoxml->asXML();
 
?>

Bastante más sencillo con SimpleXML, no? Como siempre PHP simplificando la vida a los sufridos developers 🙂

Preguntas de examen

1. Given the following XML document in a SimpleXML object:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>XML Example</title>
</head>
<body>
<p>
Moved to &lt;<a href="http://example.org/">http://www.example.org/</a>.&gt;
<br/>
</p>
</body>
</html>

Select the proper statement below which will display the HREF attribute of the anchor tag.

Posibilidades:
$sxe­->body­->p[0]­->a[1][‘href’]
$sxe­->body­->p­->a­->href
$sxe­->body­->p­->a[‘href’]
$sxe[‘body’][‘p’][0][‘a’][‘href’]
$sxe­->body­->p[1]­->a[‘href’]

Spoiler
De las opciones que proponen, la única buena es $sxe­->body­->p­->a[‘href’]

2. SimpleXML objects can be created from what types of data sources?

Opciones:
– A String
– An array
– A DomDocument object
– A URI
– A Database resource

Spoiler
String, DomDocument y URI

3. What does the following PHP script accomplish?

<?php
$dom = new DomDocument();
$dom->load('test.xml');
$body = $dom->documentElement->getElementsByTagName('body')->item(0);
echo $body->getAttributeNode('background')->value."\n";
?>

Opciones

– Displays the content of every “body” node
– Displays the “background” attribute for the first node in the XML document named “body”
– Displays the content of every node that has a “background” node
– Displays the “background” attribute of every node named “body”

Spoiler
La segunda, muestra el atributo background del primer nodo llamado body en el documento XML

4. Consider the following PHP script fragment:

<?php
$title = $dom->createElement('title');
$node = ????????
$title->appendChild($node);
$head->appendChild($title);
?>

5. What should ??????? be replaced with to add a title node with the value of Hello, World!

Opciones:

– $dom­>createTextNode(“Hello, World”);
– $dom­>appendElement($title, “text”, “Hello, world!”);
– $dom­>appendTextNode($title, “Hello, World!”);
– $dom­>createElement(‘text’, “Hello, World”);
– None of the above

Spoiler
Aix aix… cuantas dudas. Esta es una putada. appendElement no existe, appendTextNode tampoco. Create element es correcto pero nos haría <text>Hello World</text> así que la buena es la de createTextNode

6. When working with SimpleXML in PHP 5, the four basic rules on how the XML document is accessed are which of the following?

– Element namespaces are denoted by the ‘namespace’ attribute
– Converting an element to a string denotes text data
– Non-numeric indexes are element attributes
– Numeric indexes are elements
– Properties denote element iterators

Spoiler
Por eliminación podemos sacarlo, pero analicemos. Los atributos son las claves no numéricas y los elementos van con claves numéricas de contador. Lo de los iterators puede hacernos dudar, pero efectivamente podemos hacer un foreach sobre las propiedades de un nodo. Y convirtiendo un elemento a string (por ejemplo con la función strval o cualquier otra que provoque la ejecución del __toString interno sacaremos su texto interno. Además, la primera no es correcta, la función para recuperar los namespaces es getNamespaces. Por tanto B,C,D,E.

Vaya post más largo! Con esto terminamos el repaso al tratamiento de XML, en unos días vuelvo con la segunda parte de Memcached!

You may also like...

1 Response

  1. luis says:

    excelente,..espero sigas con los post de las certificaciones php 5.3 claro continuando estos q son formidables,.. es cierto que son muchos temas pero asi a paso seguro tambien reviso lo tuyo 🙂 sige con esta labor formidable. Saludos. desde Perú.