Manuel PHP

Précédent

 

Suivant

LIII. Analyseur syntaxique XML

Introduction

Le XML

Le langage XML (eXtensible Markup Language (Langage balisé étendu)) est un format structuré de données pour les échanges sur le web. C'est un standard défini par le consortium World Wide Web (W3C). Plus d'informations à propos du XML et des technologies afférentes sont accessibles (en anglais) http://www.w3.org/XML/.

Installation

Cette extension de PHP utilise expat, disponible à http://www.jclark.com/xml/. Le fichier Makefile livré avec expat ne construit pas par défaut de librairie : il faut utiliser la ligne suivante :

libexpat.a: $(OBJS)
	ar -rc $@ $(OBJS)
	ranlib $@

Les sources de expat sont disponibles à http://www.guardian.no/~ssb/phpxml.html.

Sous UNIX, configurez PHP avec l'option -xml, la librairie expat étant installée là où votre compilateur peut la trouver. Il vous faudra peut être utiliser les flags CPPFLAGS et LDFLAGS dans votre environnement avant de lancer la configuration, si vous avez déjà fait une installation exotique de expat.

Compilez PHP. Tada! C'est fait !

A propos de cette extension :

Cette extension PHP supporte la librairie expat de James Clark sous PHP. Cela vous permettra d'analyser mais pas de valider les documents XML. Il supporte trois types de codage différents, disponibles aussi sous PHP: US-ASCII, ISO-8859-1 et UTF-8. UTF-16 n'est pas supporté.

Cette extension vous permet de créer des analyseurs puis de définir des points d'entrée pour chaque événement XML. Les analyseurs XML dispose de quelques paramétrage.

Mes gestionnaires d'évènements XML sont:

Table 1. Gestionnaires d'évenements XML

Fonction PHP de configuration du gestionnaire

Description de l'événement

xml_set_element_handler()

Un événement est généré à chaque fois que l'analyseur XML rencontre une balise de début ou de fin. Deux gestionnaires sont disponibles : un pour le début, et un pour la fin.

xml_set_character_data_handler()

" Character data " correspond grosso modo à tout ce qui n'est pas une balise XML, y compris les espaces entre les balises. Notez bien que l'analyseur XML n'ajoute ou n'efface aucun espace, et que c'est à l'application (c'est à dire vous) de décider de la signification de ces espaces..

xml_set_processing_instruction_handler()

Les programmeurs PHP sont habitués aux instructions exécutables (processing instructions ou PIs). <?php ?> est une instruction exécutable où php est appelé programme cible. Ces instructions sont gérées de manière spécifiques, (sauf le programme cible, qui est réservé à XML).

xml_set_default_handler()

Tout ce qui n'a pas trouvé de gestionnaire est transmis au gestionnaire par défaut. Vous retrouverez par exemple, les déclarations de type de document dans ce gestionnaire..

xml_set_unparsed_entity_decl_handler()

Ce gestionnaire est appelé pour gérer les déclaration des entités non analysés.

xml_set_notation_decl_handler()

Ce gestionnaire est appelé pour gérer les notations.

xml_set_external_entity_ref_handler()

Ce gestionnaire est appelé lorsque l'analyseur XML trouve une référence à un fichier externe. Cela peut être un fichier, ou une URL. Reportez vous à entité externe pour un exemple.

Problèmes de majuscule/minuscule

Les fonctions de gestion des balises peuvent rencontrer des balises en minuscule, majuscule ou encore dans un mélange des deux. En XML, la procédure standard est d' "identifier les séquences de caractère qui ne sont pas reconnues comme majuscule, et de les remplacer par leur équivalent majuscule". En d'autres termes, XML met toutes lettres en majuscules.

Par défaut, tous les noms des éléments qui sont transmis aux fonctions de gestion sont mises en majuscule. Ce comportement est contrôlé par l'analyseur XML, et peut être lu et modifié avec les fonctions respectives xml_parser_get_option() et xml_parser_set_option().

Codes d'erreurs

Les constantes suivantes sont définies comme des codes d'erreurs XML : (retournée par xml_parse()):

XML_ERREUR_NONE

XML_ERREUR_NO_MEMORY

XML_ERREUR_SYNTAX

XML_ERREUR_NO_ELEMENTS

XML_ERREUR_INVALID_TOKEN

XML_ERREUR_UNCLOSED_TOKEN

XML_ERREUR_PARTIAL_CHAR

XML_ERREUR_TAG_MISMATCH

XML_ERREUR_DUPLICATE_ATTRIBUTE

XML_ERREUR_JUNK_AFTER_DOC_ELEMENT

XML_ERREUR_PARAM_ENTITY_REF

XML_ERREUR_UNDEFINED_ENTITY

XML_ERREUR_RECURSIVE_ENTITY_REF

XML_ERREUR_ASYNC_ENTITY

XML_ERREUR_BAD_CHAR_REF

XML_ERREUR_BINARY_ENTITY_REF

XML_ERREUR_ATTRIBUTE_EXTERNAL_ENTITY_REF

XML_ERREUR_MISPLACED_XML_PI

XML_ERREUR_UNKNOWN_ENCODING

XML_ERREUR_INCORRECT_ENCODING

XML_ERREUR_UNCLOSED_CDATA_SECTION

XML_ERREUR_EXTERNAL_ENTITY_HANDLING

Codage des caractères

L'extension XML de PHP supporte les caractères Unicode grâce à différents codages. Il y a deux types de codages de caractères : le codage à la source et le codage à la cible. PHP utilise le UTF-8 comme représentation interne.

L'encodage à la source est effectué lors de l'analyse du fichier par XML. Lors de l'appel de creating an XML parser (création d'un analyseur XML), un type de codage à la source doit être spécifié (et il ne pourra plus être modifié jusqu'à la destruction de l'analyseur). Les codages supportés sont : ISO-8859-1, US-ASCII et UTF-8. Les deux deniers sont des codages à un seul octet, c'est à dire que les caractères sont représentés sur un seul octet. UTF-8 peut représenter des caractères composés par une nombre variable de bits (jusqu'à 21), allant de 1 à quatres octets. Le codage par défaut utilisé par PHP ISO-8859-1.

Le codage à la cible est effectué lorsque PHP transfert les données aux gestionnaires XML. Lorsqu'un analyseur est créé, le codage à la cible est spécifié de la même façon que le codage à la source, mais il peut être modifié à tout moment. Le codage à la cible affectera les balises, tout comme les données brutes, et les noms des instructions exécutables.

Si l'analyseur XML rencontre un caractère qu'il ne connaît pas (hors limite, par exemple), il retournera une erreur.

Si PHP rencontre un caractère dans le document XML analysé, qu'il ne peut pas représenter dans le codage à la cible choisi, le caractère sera remplacé par un point d'interrogation (cette attitude est suceptible de changer ultérieurement).

Queles exemples

Voici une liste d'exemple de code PHP qui analyse un document XML.

Structure des éléments XML

Ce premier exemple affiche la structure de l'élément de début dans un document avec indentation

XML Transtypage XML -> HTML

Exemple 2. Transtypage XML -> HTML

Cet exemple remplace les balises XML d'un document par des balises HTML. Les éléments inconnus seront ignorés. Bien entendu, cet exemple sera applique à un type précis de fichiers XML.

$file = "data.xml";
$map_array = array(
    "BOLD"     => "B",
    "EMPHASIS" => "I",
    "LITERAL"  => "TT"
);

function elementDebut($parser, $name, $attrs)
{
    global $map_array;
    if ($htmltag = $map_array[$name]) {
        print "<$htmltag>";
    }
}

function elementFin($parser, $name)
{
    global $map_array;
    if ($htmltag = $map_array[$name]) {
        print "</$htmltag>";
    }
}

function characterData($parser, $data)
{
    print $data;
}

$xml_parser = xml_parser_create();
// use case-folding so we are sure to find the tag in $map_array
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, "elementDebut", "elementFin");
xml_set_character_data_handler($xml_parser, "characterData");
if (!($fp = fopen($file, "r"))) {
    die("could not open XML input");
}
while ($data = fread($fp, 4096)) {
    if (!xml_parse($xml_parser, $data, feof($fp))) {
        die(sprintf("erreur XML: %s à la ligne %d",
                    xml_erreur_string(xml_get_erreur_code($xml_parser)),
                    xml_get_current_line_number($xml_parser)));
    }
}
xml_parser_free($xml_parser);

XML Entité externe

Cet exemple exploite les références externes de XML : il est possible d'utiliser un gestionnaire d'entité externe pour inclure et analyser les documents, tous comme les instructions exécutables peuvent servir à inclure et analyser d'autres documents, et aussi fournir une indication de confiance (voir plus bas).

Le document XML qui est utilisé dans cet exemple est fourni plus loin dans l'exemple (xmltest.xml et xmltest2.xml.)

Exemple 3. Entité externe

$file = "xmltest.xml";

function trustedFile($file)
{
    // only trust local files owned by ourselves
    if (!eregi("^([a-z]+)://", $file) && fileowner($file) == getmyuid()) {
        return true;
    }
    return false;
}

function elementDebut($parser, $name, $attribs)
{
    print "&lt;<font color=\"#0000cc\">$name</font>";
    if (sizeof($attribs)) {
        while (list($k, $v) = each($attribs)) {
            print " <font color=\"#009900\">$k</font>=\"<font color=\"#990000\">$v</font>\"";
        }
    }
    print "&gt;";
}

function elementFin($parser, $name)
{
    print "&lt;/<font color=\"#0000cc\">$name</font>&gt;";
}

function characterData($parser, $data)
{
    print "<b>$data</b>";
}

function PIHandler($parser, $target, $data)
{
    switch (strtolower($target)) {
        case "php":
            global $parser_file;
            // Si le document analysé est "fiable", on peut penser qu'il 
            // est bon pour être exécuté comme code PHP.  Si non, on
            // affiche simplement le code.
            if (trustedFile($parser_file[$parser])) {
                eval($data);
            } else {
                printf("code PHP non fiable: <i>%s</i>", htmlspecialchars($data));
            }
            break;
    }
}

function defaultHandler($parser, $data)
{
    if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
        printf('<font color="#aa00aa">%s</font>', htmlspecialchars($data));
    } else {
        printf('<font size="-1">%s</font>', htmlspecialchars($data));
    }
}

function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId,
                                  $publicId)
{
    if ($systemId) {
        if (!list($parser, $fp) = new_xml_parser($systemId)) {
            printf("Impossible d'accéder à l'entité %s à la ligne %s\n", $openEntityNames,
                   $systemId);
            return false;
        }
        while ($data = fread($fp, 4096)) {
            if (!xml_parse($parser, $data, feof($fp))) {
                printf("XML erreur: %s à la ligne %d lors de l'analyse de l'entité %s\n",
                       xml_erreur_string(xml_get_erreur_code($parser)),
                       xml_get_current_line_number($parser), $openEntityNames);
                xml_parser_free($parser);
                return false;
            }
        }
        xml_parser_free($parser);
        return true;
    }
    return false;
}


function new_xml_parser($file) {
    global $parser_file;

    $xml_parser = xml_parser_create();
    xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1);
    xml_set_element_handler($xml_parser, "elementDebut", "elementFin");
    xml_set_character_data_handler($xml_parser, "characterData");
    xml_set_processing_instruction_handler($xml_parser, "PIHandler");
    xml_set_default_handler($xml_parser, "defaultHandler");
    xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
    
    if (!($fp = @fopen($file, "r"))) {
        return false;
    }
    if (!is_array($parser_file)) {
        settype($parser_file, "array");
    }
    $parser_file[$xml_parser] = $file;
    return array($xml_parser, $fp);
}

if (!(list($xml_parser, $fp) = new_xml_parser($file))) {
    die("could not open XML input");
}

print "<pre>";
while ($data = fread($fp, 4096)) {
    if (!xml_parse($xml_parser, $data, feof($fp))) {
        die(sprintf("XML erreur: %s à la ligne %d\n",
                    xml_erreur_string(xml_get_erreur_code($xml_parser)),
                    xml_get_current_line_number($xml_parser)));
    }
}
print "</pre>";
print "parse complete\n";
xml_parser_free($xml_parser);

?>

Exemple 4. xmltest.xml

<?xml version='1.0'?>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml">
]>
<chapter>
 <TITLE>Title &plainEntity;</TITLE>
 <para>
  <informaltable>
   <tgroup cols="3">
    <tbody>
     <row><entry>a1</entry><entry morerows="1">b1</entry><entry>c1</entry></row>
     <row><entry>a2</entry><entry>c2</entry></row>
     <row><entry>a3</entry><entry>b3</entry><entry>c3</entry></row>
    </tbody>
   </tgroup>
  </informaltable>
 </para>
 &systemEntity;
 <sect1 id="about">
  <title>About this Document</title>
  <para>
   <!-- this is a comment -->
   <?php print 'Hi!  This is PHP version '.phpversion(); ?>
  </para>
 </sect1>
</chapter>

Ce fichier est inclus à partir du fichier xmltest.xml:

Exemple 5. xmltest2.xml

<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY testEnt "test entity">
]>
<foo>
 <element attrib="value"/>
 &testEnt;
 <?php print "This is some more PHP code being executed."; ?>
</foo>

Table des matières
xml_parser_create _ Création d'un analyseur XML
xml_set_element_handler _ Affecte les gestionnaires de début et de fin
xml_set_character_data_handler _ Affecte les gestionnaires de caractère bruts
xml_set_processing_instruction_handler _ Affecte les gestionnaires de instruction exécutables (PI)
xml_set_default_handler _ Affecte le gestionnaire par défaut
xml_set_unparsed_entity_decl_handler _ Affecte les gestionnaires d'entité non déclaré
xml_set_notation_decl_handler _ Affecte les gestionnaires de notation
xml_set_external_entity_ref_handler _ Affecte les gestionnaires d'entité externe
xml_parse _ Commence l'analyse d'un fichier XML
xml_get_erreur_code _ Retourne le code d'erreur XML
xml_erreur_string _ Retourne le message d'erreur XML
xml_get_current_line_number _ Retourne le nombre courant de ligne d'un analyseur XML
xml_get_current_column_number _ Retourne le nombre courant de colonne d'un analyseur XML.
xml_get_current_byte_index _ Retourne l'index de l'octet courant d'un analyseur XML
xml_parser_free _ Détruit un analyseur XML
xml_parser_set_option _ Affecte les options d'un analyseur XML
xml_parser_get_option _ Lit les options d'un analyseur XML
utf8_decode _ Converti un une chaîne UTF-8 en ISO-8859-1
utf8_encode _ Converti un une chaîne ISO-8859-1 en UTF-8

Précédent

Sommaire

Suivant

gzwrite

Chapitre

xml_parser_create