Valider ef1df8b2 rédigé par esj's avatar esj
Parcourir les fichiers

Le validateur déclarait forfait pour un squelette dépourvu de DOCTYPE...

Le validateur déclarait forfait pour un squelette dépourvu de DOCTYPE (typiquement les inclus), alors qu'il opérait sans problème pour les scripts de l'espace privé sans DOCTYPE (typiquement les AJAX). On harmonise.

Ces prises en compte supplémentaires cumulées avec l'abandon de {{{recuperer_page}}}, au dépot précédent ont amené des pbs de saturation de mémoire dans le processus de validation en boucle, du coup on optimise. En pratique élimination de {{{ereg}}}).(décidément pas fiable) et des variables globales (sauf une; à terminer). 
parent f3bf27c2
Chargement en cours
Chargement en cours
Chargement en cours
Chargement en cours
+14 −10
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -16,29 +16,28 @@ class IndenteurXML {

// http://doc.spip.org/@debutElement
function debutElement($phraseur, $name, $attrs)
{ xml_debutElement($phraseur, $name, $attrs);}
{ xml_debutElement($this, $name, $attrs);}

// http://doc.spip.org/@finElement
function finElement($phraseur, $name)
{ xml_finElement($phraseur, $name);}
{ xml_finElement($this, $name);}

// http://doc.spip.org/@textElement
function textElement($phraseur, $data)
{ xml_textElement($phraseur, $data);}
{ xml_textElement($this, $data);}

// http://doc.spip.org/@PiElement
function PiElement($phraseur, $target, $data)
{ xml_PiElement($phraseur, $target, $data);}
{ xml_PiElement($this, $target, $data);}

// http://doc.spip.org/@defautElement
function defautElement($phraseur, $data)
{  xml_defautElement($phraseur, $data);}
{  xml_defautElement($this, $data);}

// http://doc.spip.org/@phraserTout
function phraserTout($phraseur, $data)
{
	xml_parsestring($phraseur, $data);
	return !$this->err ?  $this->res : join('<br />', $this->err) . '<br />';
	xml_parsestring($this, $data);
}

 var $depth = "";
@@ -47,16 +46,21 @@ function phraserTout($phraseur, $data)
 var $contenu = array();
 var $ouvrant = array();
 var $reperes = array();

 var $entete = '';
 var $page = '';
 var $dtc = NULL;
 var $sax = NULL;
}

// http://doc.spip.org/@xml_indenter_dist
function xml_indenter_dist($page, $apply=false)
{
	$sax = charger_fonction('sax', 'xml');
	return $sax($page, $apply, $GLOBALS['phraseur_xml'] = new IndenteurXML());

	$f = new IndenteurXML();
	$sax($page, $apply, $f);
	if (!$f->err) return $f->entete . $f->res;
	spip_log("indentation impossible " . count($f->err) . " erreurs de validation");
	return $f->entete . $f->page;
}

?>
+77 −98
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -17,25 +17,21 @@ include_spip('inc/charsets');
include_spip('xml/interfaces');

// http://doc.spip.org/@xml_debutElement
function xml_debutElement($parser, $name, $attrs)
function xml_debutElement($phraseur, $name, $attrs)
{
	global $phraseur_xml;
	$depth = &$phraseur_xml->depth;
	$contenu = &$phraseur_xml->contenu;
	$ouvrant = &$phraseur_xml->ouvrant;
	$reperes = &$phraseur_xml->reperes;
	$depth = $phraseur->depth;

	$t = isset($ouvrant[$depth]) ? $ouvrant[$depth] : ' ';
	$t = isset($phraseur->ouvrant[$depth]) ? $phraseur->ouvrant[$depth] : ' ';
	// espace initial signifie: deja integree au resultat
	if ($t[0] != ' ')
	  {
	    $phraseur_xml->res .= '<' . $t . '>';
	    $ouvrant[$depth] = ' ' . $t;
	    $phraseur->res .= '<' . $t . '>';
	    $phraseur->ouvrant[$depth] = ' ' . $t;
	  }
	$t = $contenu[$depth];
	$t = $phraseur->contenu[$depth];
	// n'indenter que s'il y a un separateur avant
	$phraseur_xml->res .= ereg_replace("[\n\t ]+$",  "\n$depth", $t);
	$contenu[$depth] = "";
	$phraseur->res .= preg_replace("/[\n\t ]+$/",  "\n$depth", $t);
	$phraseur->contenu[$depth] = "";
	$att = '';
	$sep = ' ';
	foreach ($attrs as $k => $v) {
@@ -46,28 +42,23 @@ function xml_debutElement($parser, $name, $attrs)
	    . $delim;
	  $sep = "\n $depth";
	}
	$depth .= '  ';
	$contenu[$depth] = "";
	$ouvrant[$depth] = $name . $att;
	$reperes[$depth] = xml_get_current_line_number($parser);
	$phraseur->depth .= '  ';
	$phraseur->contenu[$phraseur->depth] = "";
	$phraseur->ouvrant[$phraseur->depth] = $name . $att;
	$phraseur->reperes[$phraseur->depth] = xml_get_current_line_number($phraseur->sax);
}

// http://doc.spip.org/@xml_finElement
function xml_finElement($parser, $name, $fusion_bal=false)
function xml_finElement($phraseur, $name, $fusion_bal=false)
{
	global $phraseur_xml;
	$depth = &$phraseur_xml->depth;
	$contenu = &$phraseur_xml->contenu;
	$ouvrant = &$phraseur_xml->ouvrant;

	$ouv = $ouvrant[$depth];
	$ouv = $phraseur->ouvrant[$phraseur->depth];

	if ($ouv[0] != ' ')
	  $ouvrant[$depth] = ' ' . $ouv;
		$phraseur->ouvrant[$phraseur->depth] = ' ' . $ouv;
	else $ouv= "";
	$t = $contenu[$depth];
	$depth = substr($depth, 2);
	$t = ereg_replace("[\n\t ]+$", "\n" . $depth, $t);
	$t = $phraseur->contenu[$phraseur->depth];
	$phraseur->depth = substr($phraseur->depth, 2);
	$t = preg_replace("/[\n\t ]+$/", "\n" . $phraseur->depth, $t);

  // fusion <balise></balise> en <balise />.
  // ATTENTION,  certains clients http croient que fusion ==> pas d'atttributs
@@ -76,93 +67,83 @@ function xml_finElement($parser, $name, $fusion_bal=false)
  // (param fusion_bal)

	if ($t || (($ouv != $name) AND !$fusion_bal))
	  $phraseur_xml->res .= ($ouv ? ('<' . $ouv . '>') : '') . $t . "</" . $name . ">";
	  $phraseur->res .= ($ouv ? ('<' . $ouv . '>') : '') . $t . "</" . $name . ">";
	else
	  $phraseur_xml->res .= ($ouv ? ('<' . $ouv  . ' />') : ("</" .  $name . ">"));
	  $phraseur->res .= ($ouv ? ('<' . $ouv  . ' />') : ("</" .  $name . ">"));
}

// http://doc.spip.org/@xml_textElement
function xml_textElement($parser, $data)
function xml_textElement($phraseur, $data)
{
	global $phraseur_xml;

	$depth = &$phraseur_xml->depth;
	$contenu = &$phraseur_xml->contenu;
	$contenu[$depth] .= preg_match('/^script/',$phraseur_xml->ouvrant[$depth])
	$depth = $phraseur->depth;
	$phraseur->contenu[$depth] .= preg_match('/^script/',$phraseur->ouvrant[$depth])
	  ? $data
	  : entites_html($data);
}

// http://doc.spip.org/@xml_PiElement
function xml_PiElement($parser, $target, $data)
function xml_PiElement($phraseur, $target, $data)
{
	global $phraseur_xml;
	$depth = &$phraseur_xml->depth;
	$contenu = &$phraseur_xml->contenu;
	$depth = $phraseur->depth;

	if (strtolower($target) != "php")
	  $contenu[$depth] .= $data;
	  $phraseur->contenu[$depth] .= $data;
	else {
		ob_start();
		eval($data);
		$data = ob_get_contents();
		ob_end_clean();
		$contenu[$depth] .= $data;
		$phraseur->contenu[$depth] .= $data;
	}
}


// http://doc.spip.org/@xml_defautElement
function xml_defautElement($parser, $data)
function xml_defautElement($phraseur, $data)
{
	global $phraseur_xml;
	$depth = &$phraseur_xml->depth;
	$contenu = &$phraseur_xml->contenu;
	$depth = $phraseur->depth;

	if (!isset($contenu[$depth])) $contenu[$depth]='';
	$contenu[$depth] .= $data;
	if (!isset($phraseur->contenu[$depth])) $phraseur->contenu[$depth]='';
	$phraseur->contenu[$depth] .= $data;
}

// http://doc.spip.org/@xml_parsestring
function xml_parsestring($phraseur, $data)
{
	global $phraseur_xml;
	$phraseur_xml->contenu[$phraseur_xml->depth] ='';
	$phraseur->contenu[$phraseur->depth] ='';

	if (!xml_parse($phraseur, $data, true)) {
	if (!xml_parse($phraseur->sax, $data, true)) {
	  // ne pas commencer le message par un "<" (cf xml_sax_dist)
	  $phraseur_xml->err = array(
	    xml_error_string(xml_get_error_code($phraseur)) .
	  $phraseur->err = array(
	    xml_error_string(xml_get_error_code($phraseur->sax)) .
		  coordonnees_erreur($phraseur) . "<br />\n" .
		  (!$phraseur_xml->depth ? '' :
		  (!$phraseur->depth ? '' :
		   (
		    _T('erreur_balise_non_fermee') .
		    " <tt>" .
		    $phraseur_xml->ouvrant[$phraseur_xml->depth] .
		    $prhaseur->ouvrant[$phraseur->depth] .
		    "</tt> " .
		    _T('ligne') .
		    $phraseur_xml->reperes[$phraseur_xml->depth] .
		    $phraseur->reperes[$phraseur->depth] .
		    " <br />\n" )));
	}
}

// http://doc.spip.org/@coordonnees_erreur
function coordonnees_erreur($xml_parser)
function coordonnees_erreur($phraseur)
{
  global $xml_entete_length;
  return
    ' ' .
	xml_get_current_line_number($xml_parser) + $xml_entete_length.
	$entete_length = substr_count($phraseur->entete,"\n");
	return ' ' .
		xml_get_current_line_number($phraseur->sax) + $entete_length.
		' ' .
	xml_get_current_column_number($xml_parser);
		xml_get_current_column_number($phraseur->sax);
}

// http://doc.spip.org/@xml_sax_dist
function xml_sax_dist($page, $apply=false)
function xml_sax_dist($page, $apply=false, $phraseur=NULL)
{
	global $phraseur_xml;

	// init par defaut si pas fait (espace public)
	if (!isset($GLOBALS['phraseur_xml'])) {
	// init par defaut si pas fait (compatibilite Tidy espace public)
	if (!$phraseur) {
		$indenter_xml = charger_fonction('indenter', 'xml');
		return $indenter_xml($page, $apply);
	}
@@ -170,22 +151,20 @@ function xml_sax_dist($page, $apply=false)
	$xml_parser = xml_parser_create($GLOBALS['meta']['charset']);

	xml_set_element_handler($xml_parser,
			array($phraseur_xml, "debutElement"),
			array($phraseur_xml, "finElement"));
			array($phraseur, "debutElement"),
			array($phraseur, "finElement"));

	xml_set_character_data_handler($xml_parser,
				       array($phraseur_xml, "textElement"));
				       array($phraseur, "textElement"));

	xml_set_processing_instruction_handler($xml_parser,
				       array($phraseur_xml, 'PiElement'));
				       array($phraseur, 'PiElement'));

	xml_set_default_handler($xml_parser,
				array($phraseur_xml, "defautElement"));
				array($phraseur, "defautElement"));

	xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);

	unset($GLOBALS['xhtml_error']);

	if ($apply) {
		ob_start();
		if (is_array($apply))
@@ -198,20 +177,15 @@ function xml_sax_dist($page, $apply=false)
	}

	// charger la DTD et transcoder les entites,
	// et escamoter le doctype que sax mange en php 5
	list($entete,$page) = sax_bug($page);

	$GLOBALS['xml_entete_length'] = substr_count($entete,"\n");

	$res = $phraseur_xml->phraserTout($xml_parser, $page);

	// et escamoter le doctype que sax mange en php5 mais pas en  php4
	list($entete,$page, $dtc) = sax_bug($page);

	$phraseur->sax = $xml_parser;
	$phraseur->entete = $entete;
	$phraseur->page = $page;
	$phraseur->dtc = $dtc;
	$phraseur->phraserTout($xml_parser, $page);
	xml_parser_free($xml_parser);

	if ($res[0] == '<') return $entete . $res;

	$GLOBALS['xhtml_error'] = $res;

	return $entete . $page;
}

// SAX ne dit pas si une Entite est dans un attribut ou non.
@@ -224,7 +198,7 @@ function xml_sax_dist($page, $apply=false)
// http://doc.spip.org/@sax_bug
function sax_bug($data)
{
	global  $phraseur_xml;
	static $dtd = array(); # cache bien utile pour le validateur en boucle

	$r = analyser_doctype($data);

@@ -238,23 +212,28 @@ function sax_bug($data)

	$file = _DIR_CACHE_XML . preg_replace('/[^\w.]/','_', $rotlvl) . '.gz';

	if (isset($dtd[$file]))
		$dtc = $dtd[$file];
	else {
		if (lire_fichier($file, $r)) {
			$phraseur_xml->dtc = unserialize($r);
			$dtc = unserialize($r);
		} else {
			include_spip('xml/analyser_dtd');
		$phraseur_xml->dtc = charger_dtd($grammaire, $avail);
		if (($avail == 'PUBLIC' ) AND $phraseur_xml->dtc)
			ecrire_fichier($file, serialize($phraseur_xml->dtc), true);
			$dtc = charger_dtd($grammaire, $avail);
			if (($avail == 'PUBLIC' ) AND $dtc)
				ecrire_fichier($file, serialize($dtc), true);
		}
		$dtd[$file] = $dtc;
	}

	// l'entete contient eventuellement < ? xml... ? >, le Doctype, 
	// et des commentaires autour d'eux
	$entete = ltrim(substr($data,0,$len));

	if ($phraseur_xml->dtc) {
	if ($dtc) {
		$trans = array();
		
		foreach($phraseur_xml->dtc->entites as $k => $v) {
		foreach($dtc->entites as $k => $v) {
			if (!strpos(" amp lt gt quot ", $k))
			    $trans["&$k;"] = $v;
		}
@@ -262,7 +241,7 @@ function sax_bug($data)
	} else {
		$data = html2unicode(substr($data,$len), true);
	}
	return array($entete,unicode2charset($data));
	return array($entete,unicode2charset($data), $dtc);
}

// http://doc.spip.org/@analyser_doctype
+96 −107
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
@@ -12,34 +12,38 @@

if (!defined("_ECRIRE_INC_VERSION")) return;

// Validateur XML en deux passes, fonde sur SAX pour la premiere
// Faudrait faire deux classes car pour la premiere passe
// on a les memes methodes et variables que l'indenteur

class ValidateurXML {

// http://doc.spip.org/@validerElement
function validerElement($phraseur, $name, $attrs)
{
	global $phraseur_xml;

	if (!($p = isset($phraseur_xml->dtc->elements[$name]))) {
	if (!($p = isset($this->dtc->elements[$name]))) {
		if ($p = strpos($name, ':')) {
			$name = substr($name, $p+1);
			$p = isset($phraseur_xml->dtc->elements[$name]);
			$p = isset($this->dtc->elements[$name]);
		}
		if (!$p) {
			$phraseur_xml->err[]= " <b>$name</b> "
			$this->err[]= " <b>$name</b> "
			  . _T('zxml_inconnu_balise')
			  . ' '
			  .  coordonnees_erreur($phraseur);
			  .  coordonnees_erreur($this);
			return; 
		}
	}
	// controler les filles illegitimes, ca suffit 
	$depth = $phraseur_xml->depth;
	$ouvrant = $phraseur_xml->ouvrant;
	$depth = $this->depth;
	$ouvrant = $this->ouvrant;
#	spip_log("trouve $name apres " . $ouvrant[$depth]);
	if (isset($ouvrant[$depth])) {
	    if (preg_match('/^\s*(\w+)/', $ouvrant[$depth], $r)) {
	      $pere = $r[1];
#	      spip_log("pere $pere");
	      if (isset($phraseur_xml->dtc->elements[$pere])) {
		$fils = $phraseur_xml->dtc->elements[$pere];
	      if (isset($this->dtc->elements[$pere])) {
		$fils = $this->dtc->elements[$pere];
#		spip_log("rejeton $name fils " . @join(',',$fils));
		if (!($p = @in_array($name, $fils))) {
			if ($p = strpos($name, ':')) {
@@ -48,32 +52,32 @@ function validerElement($phraseur, $name, $attrs)
			}
		}
		if (!$p) {
	          $bons_peres = @join ('</b>, <b>', $phraseur_xml->dtc->peres[$name]);
	          $phraseur_xml->err[]= " <b>$name</b> "
	          $bons_peres = @join ('</b>, <b>', $this->dtc->peres[$name]);
	          $this->err[]= " <b>$name</b> "
	            . _T('zxml_non_fils')
	            . ' <b>'
	            .  $pere
	            . '</b>'
	            . (!$bons_peres ? ''
	               : ('<p style="font-size: 80%"> '._T('zxml_mais_de').' <b>'. $bons_peres . '</b></p>'))
		    .  coordonnees_erreur($phraseur);
		} else if ($phraseur_xml->dtc->regles[$pere][0]=='/') {
		  $phraseur_xml->fratrie[substr($depth,2)].= "$name ";
		    .  coordonnees_erreur($this);
		} else if ($this->dtc->regles[$pere][0]=='/') {
		  $this->fratrie[substr($depth,2)].= "$name ";
		}
	      }
	    }
	}
	// Init de la suite des balises a memoriser si regle difficile
	if ($phraseur_xml->dtc->regles[$name][0]=='/')
	    $phraseur_xml->fratrie[$depth]='';
	if (isset($phraseur_xml->dtc->attributs[$name])) {
		  foreach ($phraseur_xml->dtc->attributs[$name] as $n => $v)
	if ($this->dtc->regles[$name][0]=='/')
	    $this->fratrie[$depth]='';
	if (isset($this->dtc->attributs[$name])) {
		  foreach ($this->dtc->attributs[$name] as $n => $v)
		    { if (($v[1] == '#REQUIRED') AND (!isset($attrs[$n])))
			$phraseur_xml->err[]= " <b>$n</b>"
			$this->err[]= " <b>$n</b>"
			  . '&nbsp;:&nbsp;'
			  . _T('zxml_obligatoire_attribut')
			  . " <b>$name</b>"
			  .  coordonnees_erreur($phraseur);
			  .  coordonnees_erreur($this);
		    }
	}
}
@@ -81,13 +85,11 @@ function validerElement($phraseur, $name, $attrs)
// http://doc.spip.org/@validerAttribut
function validerAttribut($phraseur, $name, $val, $bal)
{
	global $phraseur_xml;

	// Si la balise est inconnue, eviter d'insister
	if (!isset($phraseur_xml->dtc->attributs[$bal]))
	if (!isset($this->dtc->attributs[$bal]))
		return ;
		
	$a = $phraseur_xml->dtc->attributs[$bal];
	$a = $this->dtc->attributs[$bal];
	if (!isset($a[$name])) {
		$bons = join(', ',array_keys($a));
		if ($bons)
@@ -98,64 +100,58 @@ function validerAttribut($phraseur, $name, $val, $bal)
		    "'";
		$bons .= " style='font-weight: bold'";

		$phraseur_xml->err[]= " <b>$name</b> "
		$this->err[]= " <b>$name</b> "
		. _T('zxml_inconnu_attribut').' '._T('zxml_de')
		. " <a$bons>$bal</a> ("
		. _T('zxml_survoler')
		. ")"
		.  coordonnees_erreur($phraseur);
		.  coordonnees_erreur($this);
	} else{
		$type =  $a[$name][0];
		if (!preg_match('/^\w+$/', $type))
			valider_motif($phraseur, $name, $val, $bal, $type);
		else if (function_exists($f = 'validerAttribut_' . $type))
			$f($phraseur, $name, $val, $bal);
			$this->valider_motif($phraseur, $name, $val, $bal, $type);
		else if (method_exists($this, $f = 'validerAttribut_' . $type))
			$this->$f($phraseur, $name, $val, $bal);
#		else spip_log("$type type d'attribut inconnu");
	}
}

// http://doc.spip.org/@validerAttribut_ID
function validerAttribut_ID($phraseur, $name, $val, $bal)
{
	global $phraseur_xml;

	if (isset($phraseur_xml->ids[$val])) {
		list($l,$c) = $phraseur_xml->ids[$val];
		$phraseur_xml->err[]= " <p><b>$val</b> "
	if (isset($this->ids[$val])) {
		list($l,$c) = $this->ids[$val];
		$this->err[]= " <p><b>$val</b> "
		      . _T('zxml_valeur_attribut')
		      . " <b>$name</b> "
		      . _T('zxml_de')
		      . " <b>$bal</b> "
		      . _T('zxml_vu')
		      . " (L$l,C$c)"
		      .  coordonnees_erreur($phraseur);
		      .  coordonnees_erreur($this);
	} else {
		valider_motif($phraseur, $name, $val, $bal, _REGEXP_ID);
		$phraseur_xml->ids[$val] = array(xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
		$this->valider_motif($phraseur, $name, $val, $bal, _REGEXP_ID);
		$this->ids[$val] = array(xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
	}
}

// http://doc.spip.org/@validerAttribut_IDREF
function validerAttribut_IDREF($phraseur, $name, $val, $bal)
{
	global $phraseur_xml;
	$phraseur_xml->idrefs[] = array($val, xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
	$this->idrefs[] = array($val, xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
}

// http://doc.spip.org/@validerAttribut_IDREFS
function validerAttribut_IDREFS($phraseur, $name, $val, $bal)
{
	global $phraseur_xml;

	$phraseur_xml->idrefss[] = array($val, xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
	$this->idrefss[] = array($val, xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
}

// http://doc.spip.org/@valider_motif
function valider_motif($phraseur, $name, $val, $bal, $motif)
{
	global $phraseur_xml;

	if (!preg_match($motif, $val)) {
		$phraseur_xml->err[]= " <p><b>$val</b> "
		$this->err[]= " <p><b>$val</b> "
		. _T('zxml_valeur_attribut')
		. " <b>$name</b> "
		. _T('zxml_de')
@@ -163,15 +159,15 @@ function valider_motif($phraseur, $name, $val, $bal, $motif)
		. _T('zxml_non_conforme')
		. "</p><p>"
		. "<b>" . $motif . "</b></p>"
		.  coordonnees_erreur($phraseur);
		.  coordonnees_erreur($this);
	}
}

// http://doc.spip.org/@valider_idref
function valider_idref(&$own, $nom, $ligne, $col)
function valider_idref($nom, $ligne, $col)
{
	if (!isset($own->ids[$nom]))
		$own->err[]= " <p><b>$nom</b> "
	if (!isset($this->ids[$nom]))
		$this->err[]= " <p><b>$nom</b> "
		. _T('zxml_inconnu_id')
		. " "
		. $ligne
@@ -179,104 +175,96 @@ function valider_idref(&$own, $nom, $ligne, $col)
		. $col;
}

// http://doc.spip.org/@xml_valider_passe2_dist
function xml_valider_passe2_dist(&$own)
function valider_passe2()
{
	if (!$own->err) {
		foreach ($own->idrefs as $idref) {
	if (!$this->err) {
		foreach ($this->idrefs as $idref) {
			list($nom, $ligne, $col) = $idref;
			valider_idref($own, $nom, $ligne, $col);
			$this->valider_idref($nom, $ligne, $col);
		}
		foreach ($own->idrefss as $idref) {
		foreach ($this->idrefss as $idref) {
			list($noms, $ligne, $col) = $idref;
			foreach(preg_split('/\s+/', $noms) as $nom)
				valider_idref($own, $nom, $ligne, $col);
				$this->valider_idref($nom, $ligne, $col);
		}
	}
}

class ValidateurXML {

// http://doc.spip.org/@debutElement
function debutElement($phraseur, $name, $attrs)
{ 
	global $phraseur_xml;

	if ($phraseur_xml->dtc->elements)
		validerElement($phraseur, $name, $attrs);
	if ($this->dtc->elements)
		$this->validerElement($phraseur, $name, $attrs);

	xml_debutElement($phraseur, $name, $attrs);
	$depth = &$phraseur_xml->depth;
	$phraseur_xml->debuts[$depth] =  strlen($phraseur_xml->res);
	xml_debutElement($this, $name, $attrs);
	$depth = $this->depth;
	$this->debuts[$depth] =  strlen($this->res);
	foreach ($attrs as $k => $v) {
		validerAttribut($phraseur, $k, $v, $name);
		$this->validerAttribut($phraseur, $k, $v, $name);
	}
}

// http://doc.spip.org/@finElement
function finElement($phraseur, $name)
{
	global $phraseur_xml;

	$depth = &$phraseur_xml->depth;
	$contenu = &$phraseur_xml->contenu;
	$depth = $this->depth;
	$contenu = $this->contenu;

	$n = strlen($phraseur_xml->res);
	$n = strlen($this->res);
	$c = strlen(trim($contenu[$depth]));
	$k = $phraseur_xml->debuts[$depth];
	$k = $this->debuts[$depth];

	$regle = $phraseur_xml->dtc->regles[$name];
	$regle = $this->dtc->regles[$name];
	$vide = ($regle  == 'EMPTY');
	// controler que les balises devant etre vides le sont 
	if ($vide) {
		if ($n <> ($k + $c))
			$phraseur_xml->err[]= " <p><b>$name</b> "
			$this->err[]= " <p><b>$name</b> "
			. _T('zxml_nonvide_balise')
			.  coordonnees_erreur($phraseur);
			.  coordonnees_erreur($this);
	// pour les regles PCDATA ou iteration de disjonction, tout est fait
	} elseif ($regle AND ($regle != '*')) {
		if ($regle == '+') {
		    // iteration de disjonction non vide: 1 balise au -
			if ($n == $k) {
				$phraseur_xml->err[]= " <p>\n<b>$name</b> "
				$this->err[]= " <p>\n<b>$name</b> "
				  . _T('zxml_vide_balise')
				  .  coordonnees_erreur($phraseur);
				  .  coordonnees_erreur($this);
			}
		} else {
			$f = $phraseur_xml->fratrie[substr($depth,2)];
			$f = $this->fratrie[substr($depth,2)];
			if (!preg_match($regle, $f))
				$phraseur_xml->err[]= " <p>\n<b>$name</b> "
				$this->err[]= " <p>\n<b>$name</b> "
				  .  _T('zxml_succession_fils_incorrecte')
				  . '&nbsp;: <b>'
				  . $f
				  . '</b>'
				  .  coordonnees_erreur($phraseur);
				  .  coordonnees_erreur($this);
		}

	}
	xml_finElement($phraseur, $name, $vide);
	xml_finElement($this, $name, $vide);
}

// http://doc.spip.org/@textElement
function textElement($phraseur, $data)
{	
	global $phraseur_xml;
	if (trim($data)) {
		$d = $phraseur_xml->depth;
		$d = $phraseur_xml->ouvrant[$d];
		$d = $this->depth;
		$d = $this->ouvrant[$d];
		preg_match('/^\s*(\S+)/', $d, $m);
		if ($phraseur_xml->dtc->pcdata[$m[1]]) {
			$phraseur_xml->err[]= " <p><b>". $m[1] . "</b> "
		if ($this->dtc->pcdata[$m[1]]) {
			$this->err[]= " <p><b>". $m[1] . "</b> "
			. _T('zxml_nonvide_balise') // message a affiner
			.  coordonnees_erreur($phraseur);
			.  coordonnees_erreur($this);
		}
	}
	xml_textElement($phraseur, $data);
	xml_textElement($this, $data);
}

// http://doc.spip.org/@PiElement
function PiElement($phraseur, $target, $data)
{	xml_PiElement($phraseur, $target, $data);}
{	xml_PiElement($this, $target, $data);}

// Denonciation des entitees XML inconnues
// Pour contourner le bug de conception de SAX qui ne signale pas si elles
@@ -288,59 +276,60 @@ function PiElement($phraseur, $target, $data)
// http://doc.spip.org/@defautElement
function defautElement($phraseur, $data)
{	
	global $phraseur_xml;

	if (!preg_match('/^<!--/', $data)
	AND (preg_match_all('/&([^;]*)?/', $data, $r, PREG_SET_ORDER)))
		foreach ($r as $m) {
			list($t,$e) = $m;
			if (!isset($phraseur_xml->dtc->entites[$e]))
				$phraseur_xml->err[]= " <b>$e</b> "
			if (!isset($this->dtc->entites[$e]))
				$this->err[]= " <b>$e</b> "
				  . _T('zxml_inconnu_entite')
				  . ' '
				  .  coordonnees_erreur($phraseur);
				  .  coordonnees_erreur($this);
		}

	xml_defautElement($phraseur, $data);
	xml_defautElement($this, $data);
}

// http://doc.spip.org/@phraserTout
function phraserTout($phraseur, $data)
{ 
	xml_parsestring($phraseur, $data);
	xml_parsestring($this, $data);

	if (!$this->dtc OR preg_match(',^' . _MESSAGE_DOCTYPE . ',', $data)) {
		$GLOBALS['xhtml_error'] .= 'DOCTYPE ? 0 0<br />';
		$this->err[]= ('DOCTYPE ? 0 0<br />');
	} else {
		$valider_passe2 = charger_fonction('valider_passe2', 'xml');
		$valider_passe2($this);
		$this->valider_passe2($this);
	}
	return !$this->err ?  $this->res : join('<br />', $this->err) . '<br />';
}

 var $depth = "";
 var $res = "";
 var $err = array();
 var $contenu = array();
 var $ouvrant = array();
 var $reperes = array();

 var $entete = '';
 var $page = '';
 var $dtc = NULL;
 var $err = array();
 var $sax = NULL;

 var $ids = array();
 var $idrefs = array();
 var $idrefss = array();
 var $debuts = array();
 var $fratrie = array();

}

// http://doc.spip.org/@xml_valider_dist
function xml_valider_dist($page, $apply=false)
{
	spip_timer('valider');
	$sax = charger_fonction('sax', 'xml');
	$sax = $sax($page, $apply, $GLOBALS['phraseur_xml'] = new ValidateurXML());
	spip_log("validation : " . spip_timer('valider'));
	return $sax;
	$f = new ValidateurXML();
	$sax($page, $apply, $f);
	if (!$f->err) return $f->entete . $f->res;
	$GLOBALS['xhtml_error'] = join('<br />', $f->err) . '<br />';
	return $f->entete . $f->page;
}
?>