From 4788bd906251d443ea90106270114b9347752f2d Mon Sep 17 00:00:00 2001 From: "Committo,Ergo:sum" <esj@rezo.net> Date: Thu, 14 Dec 2006 09:33:22 +0000 Subject: [PATCH] =?UTF-8?q?Extension=20du=20validateur=20XML=20=C3=A0=20un?= =?UTF-8?q?=20validateur=20lexical=20selon=20la=20DTD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le petit validateur construit autour de SAX va à présent chercher la DTD indiqué par la page des espaces public ou privé, à analyser, et vérifie que toutes les balises de la page sont présentes dans la DTD, et que tous les attributs figurant dans une balise sont autorisés par la DTD. Attention, cette analyse est seulement lexicale, pas syntaxique: on ne vérifie pas qu'un attribut obligatoire est présent, ni que la succession des balises est conforme (ça laisse passer des Form imbriqués par exemple). De plus, les entités déclarées externes ne sont pas traitées. Ca permet néanmoins de répérer les fautes les plus grossières sans passer par les validateurs externes complets, qui n'ont pas accès par URL aux pages calculées par les scripts de l'espace privé faute de Login. Cette vérification s'obtient en mettant: $GLOBALS['xml_indent'] = 'sax'; dans mes_options.php. A signaler à propos de ce validateur/indenteur, un bug non résolu mais à présent identifié. Le modèle SAX appelle une fonction définissable par le programmeur (le ''handler'', en français le ''preneur'') à chaque rencontre d'un lexème, en particulier les entités HTML comme "& eacute ;" etc. Ces entités se rencontrent dans les éléments Text et dans les attributs, ce qui provoque l'appel du preneur dans 2 contextes différents sans qu'il puisse les distinguer. Il s'ensuit que l'indenteur mettra à tort dans la dernière zone Texte les entités HTML (typiquement les lettres accentuées) présentes dans les attributs de la balise suivant cette zone. Si quelqu'un a une solution pour contourner ce bug de conception de SAX, je suis ... preneur. --- .gitattributes | 1 + ecrire/inc/sax.php | 20 ++++++-- ecrire/inc/validateur.php | 99 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 ecrire/inc/validateur.php diff --git a/.gitattributes b/.gitattributes index 8dd540441f..aade0b366e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -507,6 +507,7 @@ ecrire/inc/syndic.php -text ecrire/inc/tourner.php -text ecrire/inc/traduire.php -text ecrire/inc/utils.php -text +ecrire/inc/validateur.php -text ecrire/inc/vieilles_defs.php -text ecrire/inc/virtualiser.php -text ecrire/inc/xml.php -text diff --git a/ecrire/inc/sax.php b/ecrire/inc/sax.php index 8ca87552f2..30ada8f661 100644 --- a/ecrire/inc/sax.php +++ b/ecrire/inc/sax.php @@ -10,7 +10,6 @@ * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ - if (!defined("_ECRIRE_INC_VERSION")) return; include_spip('inc/filtres'); @@ -22,6 +21,10 @@ class PhraseurXML { function debutElement($parser, $name, $attrs) { global $phraseur_xml; + + if ($phraseur_xml->elements) + validerElement($parser, $name); + $depth = &$phraseur_xml->depth; $contenu = &$phraseur_xml->contenu; $ouvrant = &$phraseur_xml->ouvrant; @@ -42,6 +45,8 @@ function debutElement($parser, $name, $attrs) $att = ''; $sep = ' '; foreach ($attrs as $k => $v) { + if ($phraseur_xml->attributs) + validerAttribut($parser, $k, $v, $name); $delim = strpos($v, "'") === false ? "'" : '"'; $val = entites_html($v); $att .= $sep . $k . "=" . $delim @@ -159,7 +164,9 @@ function xml_parsestring($xml_parser, $data) _L(" ligne ") . $phraseur_xml->reperes[$phraseur_xml->depth])); - } else $r = $phraseur_xml->res; + } else if ($phraseur_xml->err) + $r = join(', ', $phraseur_xml->err); + else $r = $phraseur_xml->res; return $r; } @@ -169,8 +176,14 @@ function xml_parsestring($xml_parser, $data) var $contenu = array(); var $ouvrant = array(); var $reperes = array(); + var $elements = array(); + var $entites = array(); + var $attributs = array(); + var $err = array(); } + + // http://doc.spip.org/@inc_sax_dist function inc_sax_dist($page, $apply=false) { global $phraseur_xml, $xml_parser; @@ -191,7 +204,8 @@ function inc_sax_dist($page, $apply=false) { $page = ob_get_contents(); ob_end_clean(); } - + if ($validateur = charger_fonction('validateur', 'inc', true)) + $validateur($page); $res = $phraseur_xml->xml_parsestring($xml_parser, $page); xml_parser_free($xml_parser); if ($res[0] != '<') diff --git a/ecrire/inc/validateur.php b/ecrire/inc/validateur.php new file mode 100644 index 0000000000..8e2c94ea68 --- /dev/null +++ b/ecrire/inc/validateur.php @@ -0,0 +1,99 @@ +<?php + +/***************************************************************************\ + * SPIP, Systeme de publication pour l'internet * + * * + * Copyright (c) 2001-2006 * + * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * + * * + * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * + * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * +\***************************************************************************/ + + +if (!defined("_ECRIRE_INC_VERSION")) return; + +define('_REGEXP_DOCTYPE', + '/^\s*<!DOCTYPE\s+(\w+)\s+(\w+)\s+(.)([^\3>]*)\3\s+(.)([^\5>]*)\5[^>]*>/'); + +function inc_validateur_dist($data) +{ + global $phraseur_xml; + + if (!preg_match(_REGEXP_DOCTYPE, $data, $r)) + return array(); + + list(,$ns, $type, $s, $nom, $s2, $grammaire) = $r; + + include_spip('inc/distant'); + $dtd = recuperer_page($grammaire); + preg_match_all('/<!ELEMENT\s+(\w+)[^>]*>/', $dtd, $r); + $phraseur_xml->elements = $r[1]; + + $res = array(); + // on ignore les entites publiques. A ameliorer a terme + if (preg_match_all('/<!ENTITY\s+%\s+([.\w]+)\s+"([^"]*)"\s*>/', $dtd, $r, PREG_SET_ORDER)) { + foreach($r as $m) { + list(,$nom, $val) = $m; + if (preg_match_all('/%([.\w]+);/', $val, $r2, PREG_SET_ORDER)) { + foreach($r2 as $m2) + $val = str_replace($m2[0], $res[$m2[1]], $val); + } + $res[$nom] = $val; + } + } + $phraseur_xml->entites = $res; + + $res = array(); + if (preg_match_all('/<!ATTLIST\s+(\S+)\s+([^>]*)>/', $dtd, $r, PREG_SET_ORDER)) { + foreach($r as $m) { + list(,$nom, $val) = $m; + if (preg_match_all('/%([.\w]+);/', $val, $r2, PREG_SET_ORDER)) { + foreach($r2 as $m2) + // parfois faux suite au non chargement des entites publiques + if ($x = $phraseur_xml->entites[$m2[1]]) + $val = str_replace($m2[0], $x, $val); + } + $att = array(); + if (preg_match_all("/\s*(\S+)\s+(([(][^)]*[)])|(\S+))\s+(\S+)(\s*'[^']*')?/", $val, $r2, PREG_SET_ORDER)) { + foreach($r2 as $m2) + $att[$m2[1]] = $m2[5]; + } + $res[$nom] = $att; + } + } + $phraseur_xml->attributs = $res; +} + +function validerElement($parser, $name) +{ + global $phraseur_xml; + + if ($phraseur_xml->elements + AND !in_array($name, $phraseur_xml->elements)) + + $phraseur_xml->err[]= $name + . ' : ' + . _L('balise inconnue ') + . _L('ligne ') + . xml_get_current_line_number($parser); +} + + +function validerAttribut($parser, $name, $val, $bal) +{ + global $phraseur_xml; + + if ($a = $phraseur_xml->attributs[$bal] + AND !isset($a[$name])) + + $phraseur_xml->err[]= $name + . ' : ' + . _L('attribut inconnu de ') + . $bal + . _L(' ligne ') + . xml_get_current_line_number($parser); +} + + +?> -- GitLab