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

Retomamos los posts relacionados con la certificación PHP (que los tengo un poco abandonados) con la parte del tratamiento de XML. En esta ocasión hablaré sobre el tratamiento de XML y las preguntas que nos pueden salir al respecto. En próximos posts hablaré de los servicios REST, XML-RPC y el siempre asqueroso, problemático, pesado pero a la vez extramadamente difundido SOAP así que si buscas información sobre estos temas tendrás que esperar :).

Este punto del examen es relativamente sencillo y la verdad es que las preguntas no suelen ser demasiado rebuscadas. Sin embargo, es bastante probable que nunca hayamos usado SAX y incluso hay mucha gente que se acostumbra a usar SimpleXML y apenas conoce las funciones DOM. Vamos a ello.

SAX, DOM y SimpleXML son simplemente formas de trabajar con los XML, los dos primeros son recomendaciones del W3C y el tercero es exclusivo de PHP. SAX va parseando el XML y se van lanzando eventos y los otros dos cargan el XML entero en memoria y podemos hacer cualquier tipo de operación con el mismo, incluso modificarlo “al vuelo”.

Debido a ello, SAX sirve para parsear XML muy grandes, que si cargásemos en memoria nos dejarían frito el servidor y los otros dos nos sirven para prácticamente cualquier aplicación que haga mensajería entre servicios web.

Resumiendo:
– SAX usa menos memoria ya que no carga el elemento entero y va parseando el XML, lanzando funciones callback en algunos eventos. No se puede cambiar nada del XML. Solamente se usa para archivos muy grandes.
– DOM y SimpleXML cargan el XML en memoria como un objeto y podemos modificarlo. Gasta algo más de memoria.

Voy a publicar aquí los ejemplos que usaba cuando hacía de profesor en PUE y BCN Activa, basados en el clásico XML de “The XML Bible” conteniendo los elementos de la tabla periódica. Podéis descargarlo aquí

SAX

Para ser sinceros, jamás he tenido que usar SAX en un proyecto de verdad. Y en los cursos, solamente un alumno me comentó que una vez lo había usado en proyectos para banca donde se trataban XMLs inmensos (varios cientos de Megas). Evitadlo siempre que podáis, ya que es extremadamente incómodo.

Ejemplo SAX:

<?php
class miParserSAX {
    /* Variables privadas para el archivo y el objeto */
    private $archivo;
    private $xmlparser; 
    /* Variables para tener contadores y apuntadores de datos */
    public $cuentaatoms = 0;
    private $tmp_text;
    private $parent_tag;
    /* Constantes para los tags en los que nos queremos fijar */
    const NUMATOMIC = "ATOMIC_NUMBER";
    const NAME = "NAME";
    public function  __construct($archivo) {
        if(!$fd = fopen($archivo, "r")) { die("Error abriendo archivo " . $archivo); }
        $this->archivo = $archivo;
        /* Creamos el objeto SAX */
        $this->xmlparser = xml_parser_create();
        /* Objeto donde están los callback */
        xml_set_object($this->xmlparser, $this);
        /* Marcamos los callback de eventos apertura y cierre */
        xml_set_element_handler($this->xmlparser, "funcinitag", "funcfintag");
        /* Marcamos el callback para el texto */
        xml_set_character_data_handler($this->xmlparser, "funcdatos");
        /* Por simplicidad, cargamos todo en memoria. 
            En archivos muy grandes habría que usar streams ya que así casi 
            gastamos tanta memoria como en DOM
        */
        $datos = fread($fd, filesize($archivo));
 
        /* Si falla el parseo debido a un XML mal formado */
        if(!xml_parse($this->xmlparser, $datos))
        {
            die ("Fallo en XML: " . xml_error_string(xml_get_error_code($this->xmlparser)));
        }
       /* Para observar la memoria */ 
       echo 'Memoria usada: ' . round(memory_get_usage() / 1024,1) . ' KB de ' . 
               round(memory_get_usage(1) / 1024,1) . ' KB';
        xml_parser_free($this->xmlparser);
    }
    /* Callback al encontrar un tag de apertura */
    public function funcinitag($parser, $nomtag, $atributos) { 
        $this->parent_tag = $nomtag;
    }
    /* Callback al encontrar un tag de cierre */
    public function funcfintag($parser, $nomtag) {
        $this->parent_tag = "";
        if($nomtag == "ATOM") { $this->cuentaatoms++; }
    }
    /* Callback al encontrar texto */
    public function funcdatos($parser, $datos) { 
        if($this->parent_tag == self::NAME)
        {
             $this->tmp_text = $datos;
        }
        if($this->parent_tag == self::NUMATOMIC)
        {
           echo "<p>NUM ATOM : " . $datos . " ELEMENTO " . $this->tmp_text . "</p>";
        }
    }
}
 
/* 
  El constructor lo hace todo... 
  En los callback vamos escribiendo el proceso
 */
$biblioteca = new miParserSAX("allelements.xml");
/* Vemos que se han procesado los N elementos */
echo $biblioteca->cuentaatoms;
 
?>

Vaya peñazo de código. Mis alumnos siempre se dormían y los pocos que lo seguían me preguntaban si no había nada más fácil.

Pues en SAX no, este sistema funciona a eventos y saltan callbacks cuando se produce un inicio de tag, un cierre de tag y el texto. Y para saber dónde estamos cuando se abre un tag hijo, tenemos que guardarnos el tag padre como se hace en el callback funcinitag.

Y todo esto código para acabar simplemente diciendo que hay N elementos y escribir los nombres y su número atómico. Un desastre, vamos, sobretodo si lo comparamos con los otros dos métodos.

De todas maneras, no debéis asustaros por este tema ya que las preguntas que os podéis encontrar son tipo las siguientes:

Pregunta 1

Consider the following code segment:

<?php
$xmldata = '<?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>';
 
$xml = xml_parser_create("UTF-8");
/* ??????? */
xml_parse($xml, $xmldata);
function xml_start_handler($xml, $tag, $attributes) {
print "Tag: $tag<br/>\n";
}
function xml_end_handler($xml, $tag) {
}
?>

What should be placed in place of ???? above to have the above script display the name of each tag
within the XML document?
Answer…

· xml_set_callback(“xml_start_handler”);
· xml_set_element_handler($xml, “xml_start_handler”, “xml_end_handler”);
· xml_node_set_handler(“xml_start_handler”, “xml_end_handler”);
· xml_node_set_handler(“xml_start_handler”);

Spoiler
La respuesta correcta sería la segunda ya que el resto no existen.

Pregunta 2

Consider the following example XML document:

<?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 <<a href="http://example.org/">http://www.example.org/</a>.>
<br>
</p>
</body>
</html>

What is wrong with this document, and how can it be corrected?

Answers: (choose 2)
· The document is completely valid
· All special XML characters must be represented as entities within the content of a node
· All tags must be closed
· You cannot specify a namespace for the attribute
· The DOCTYPE declaration is malformed

Spoiler
Las respuestas correctas son la 2 y la 3. En XHTML solamente es válido
y cuando ponemos los < o > hay que usar las entities &lt; y &gt;

Pregunta 3

Creating new nodes in XML documents using PHP can be done
using which XML/PHP 5 technologies?

· XQuery
· XPath
· SimpleXML
· DOM
· SAX

Spoiler
SimpleXML y DOM

Pregunta 4

Event­-based XML parsing is an example of which parsing model?

Spoiler
SAX

Pregunta 5

PHP 5 supports which of the following XML parsing methods? Answers: (choose 4)
· SAX
· FastDOM
· DOM
· XPath
· XML to Object mapping

Spoiler
Todas correctas menos FastDOM que no existe. Xpath está soportado como parte de la implementación DOM (veremos más sobre esto en el próximo post) y también existe el tratamiento de XML como si fuera un objeto con nodos como propiedades públicas, de forma muy similar a como lo hace SimpleXML.

Y esto es todo por hoy, el próximo post, DOM y SimpleXML!

You may also like...