[SPIP] ~v3.0.20-->v3.0.25
[lhc/web/clavette_www.git] / www / ecrire / xml / valider.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 // Validateur XML en deux passes, fonde sur SAX pour la premiere
16 // Faudrait faire deux classes car pour la premiere passe
17 // on a les memes methodes et variables que l'indenteur
18
19 class ValidateurXML {
20
21 // http://doc.spip.org/@validerElement
22 function validerElement($phraseur, $name, $attrs)
23 {
24 if (!($p = isset($this->dtc->elements[$name]))) {
25 if ($p = strpos($name, ':')) {
26 $name = substr($name, $p+1);
27 $p = isset($this->dtc->elements[$name]);
28 }
29 if (!$p) {
30 coordonnees_erreur($this," <b>$name</b>&nbsp;: "
31 . _T('zxml_inconnu_balise'));
32 return;
33 }
34 }
35 // controler les filles illegitimes, ca suffit
36 $depth = $this->depth;
37 $ouvrant = $this->ouvrant;
38 # spip_log("trouve $name apres " . $ouvrant[$depth]);
39 if (isset($ouvrant[$depth])) {
40 if (preg_match('/^\s*(\w+)/', $ouvrant[$depth], $r)) {
41 $pere = $r[1];
42 # spip_log("pere $pere");
43 if (isset($this->dtc->elements[$pere])) {
44 $fils = $this->dtc->elements[$pere];
45 # spip_log("rejeton $name fils " . @join(',',$fils));
46 if (!($p = @in_array($name, $fils))) {
47 if ($p = strpos($name, ':')) {
48 $p = substr($name, $p+1);
49 $p = @in_array($p, $fils);
50 }
51 }
52 if (!$p) {
53 $bons_peres = @join ('</b>, <b>', $this->dtc->peres[$name]);
54 coordonnees_erreur($this, " <b>$name</b> "
55 . _T('zxml_non_fils')
56 . ' <b>'
57 . $pere
58 . '</b>'
59 . (!$bons_peres ? ''
60 : ('<p style="font-size: 80%"> '._T('zxml_mais_de').' <b>'. $bons_peres . '</b></p>')));
61 } else if ($this->dtc->regles[$pere][0]=='/') {
62 $frat = substr($depth,2);
63 if (!isset($this->fratrie[$frat])) {
64 $this->fratrie[$frat] = '';
65 }
66 $this->fratrie[$frat] .= "$name ";
67 }
68 }
69 }
70 }
71 // Init de la suite des balises a memoriser si regle difficile
72 if ($this->dtc->regles[$name][0]=='/')
73 $this->fratrie[$depth]='';
74 if (isset($this->dtc->attributs[$name])) {
75 foreach ($this->dtc->attributs[$name] as $n => $v)
76 { if (($v[1] == '#REQUIRED') AND (!isset($attrs[$n])))
77 coordonnees_erreur($this, " <b>$n</b>"
78 . '&nbsp;:&nbsp;'
79 . _T('zxml_obligatoire_attribut')
80 . " <b>$name</b>");
81 }
82 }
83 }
84
85 // http://doc.spip.org/@validerAttribut
86 function validerAttribut($phraseur, $name, $val, $bal)
87 {
88 // Si la balise est inconnue, eviter d'insister
89 if (!isset($this->dtc->attributs[$bal]))
90 return ;
91
92 $a = $this->dtc->attributs[$bal];
93 if (!isset($a[$name])) {
94 $bons = join(', ',array_keys($a));
95 if ($bons)
96 $bons = " title=' " .
97 _T('zxml_connus_attributs') .
98 '&nbsp;: ' .
99 $bons .
100 "'";
101 $bons .= " style='font-weight: bold'";
102 coordonnees_erreur($this, " <b>$name</b> "
103 . _T('zxml_inconnu_attribut').' '._T('zxml_de')
104 . " <a$bons>$bal</a> ("
105 . _T('zxml_survoler')
106 . ")");
107 } else{
108 $type = $a[$name][0];
109 if (!preg_match('/^\w+$/', $type))
110 $this->valider_motif($phraseur, $name, $val, $bal, $type);
111 else if (method_exists($this, $f = 'validerAttribut_' . $type))
112 $this->$f($phraseur, $name, $val, $bal);
113 # else spip_log("$type type d'attribut inconnu");
114 }
115 }
116
117 function validerAttribut_NMTOKEN($phraseur, $name, $val, $bal)
118 {
119 $this->valider_motif($phraseur, $name, $val, $bal, _REGEXP_NMTOKEN);
120 }
121
122 function validerAttribut_NMTOKENS($phraseur, $name, $val, $bal)
123 {
124 $this->valider_motif($phraseur, $name, $val, $bal, _REGEXP_NMTOKENS);
125 }
126
127 // http://doc.spip.org/@validerAttribut_ID
128 function validerAttribut_ID($phraseur, $name, $val, $bal)
129 {
130 if (isset($this->ids[$val])) {
131 list($l,$c) = $this->ids[$val];
132 coordonnees_erreur($this, " <p><b>$val</b> "
133 . _T('zxml_valeur_attribut')
134 . " <b>$name</b> "
135 . _T('zxml_de')
136 . " <b>$bal</b> "
137 . _T('zxml_vu')
138 . " (L$l,C$c)");
139 } else {
140 $this->valider_motif($phraseur, $name, $val, $bal, _REGEXP_ID);
141 $this->ids[$val] = array(xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
142 }
143 }
144
145 // http://doc.spip.org/@validerAttribut_IDREF
146 function validerAttribut_IDREF($phraseur, $name, $val, $bal)
147 {
148 $this->idrefs[] = array($val, xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
149 }
150
151 // http://doc.spip.org/@validerAttribut_IDREFS
152 function validerAttribut_IDREFS($phraseur, $name, $val, $bal)
153 {
154 $this->idrefss[] = array($val, xml_get_current_line_number($phraseur), xml_get_current_column_number($phraseur));
155 }
156
157 // http://doc.spip.org/@valider_motif
158 function valider_motif($phraseur, $name, $val, $bal, $motif)
159 {
160 if (!preg_match($motif, $val)) {
161 coordonnees_erreur($this, "<b>$val</b> "
162 . _T('zxml_valeur_attribut')
163 . " <b>$name</b> "
164 . _T('zxml_de')
165 . " <b>$bal</b> "
166 . _T('zxml_non_conforme')
167 . "</p><p>"
168 . "<b>" . $motif . "</b>");
169 }
170 }
171
172 // http://doc.spip.org/@valider_idref
173 function valider_idref($nom, $ligne, $col)
174 {
175 if (!isset($this->ids[$nom]))
176 $this->err[]= array(" <p><b>$nom</b> " . _T('zxml_inconnu_id'), $ligne, $col);
177 }
178
179 // http://doc.spip.org/@valider_passe2
180 function valider_passe2()
181 {
182 if (!$this->err) {
183 foreach ($this->idrefs as $idref) {
184 list($nom, $ligne, $col) = $idref;
185 $this->valider_idref($nom, $ligne, $col);
186 }
187 foreach ($this->idrefss as $idref) {
188 list($noms, $ligne, $col) = $idref;
189 foreach(preg_split('/\s+/', $noms) as $nom)
190 $this->valider_idref($nom, $ligne, $col);
191 }
192 }
193 }
194
195 // http://doc.spip.org/@debutElement
196 function debutElement($phraseur, $name, $attrs)
197 {
198 if ($this->dtc->elements)
199 $this->validerElement($phraseur, $name, $attrs);
200
201 if ($f = $this->process['debut']) $f($this, $name, $attrs);
202 $depth = $this->depth;
203 $this->debuts[$depth] = strlen($this->res);
204 foreach ($attrs as $k => $v) {
205 $this->validerAttribut($phraseur, $k, $v, $name);
206 }
207 }
208
209 // http://doc.spip.org/@finElement
210 function finElement($phraseur, $name)
211 {
212 $depth = $this->depth;
213 $contenu = $this->contenu;
214
215 $n = strlen($this->res);
216 $c = strlen(trim($contenu[$depth]));
217 $k = $this->debuts[$depth];
218
219 $regle = $this->dtc->regles[$name];
220 $vide = ($regle == 'EMPTY');
221 // controler que les balises devant etre vides le sont
222 if ($vide) {
223 if ($n <> ($k + $c))
224 coordonnees_erreur($this, " <p><b>$name</b> "
225 . _T('zxml_nonvide_balise'));
226 // pour les regles PCDATA ou iteration de disjonction, tout est fait
227 } elseif ($regle AND ($regle != '*')) {
228 if ($regle == '+') {
229 // iteration de disjonction non vide: 1 balise au -
230 if ($n == $k) {
231 coordonnees_erreur($this, "<p>\n<b>$name</b> "
232 . _T('zxml_vide_balise'));
233 }
234 } else {
235 $f = $this->fratrie[substr($depth,2)];
236 if (!preg_match($regle, $f)) {
237 coordonnees_erreur($this,
238 " <p>\n<b>$name</b> "
239 . _T('zxml_succession_fils_incorrecte')
240 . '&nbsp;: <b>'
241 . $f
242 . '</b>');
243 }
244 }
245
246 }
247 if ($f = $this->process['fin']) $f($this, $name, $vide);
248 }
249
250 // http://doc.spip.org/@textElement
251 function textElement($phraseur, $data)
252 {
253 if (trim($data)) {
254 $d = $this->depth;
255 $d = $this->ouvrant[$d];
256 preg_match('/^\s*(\S+)/', $d, $m);
257 if ($this->dtc->pcdata[$m[1]]) {
258 coordonnees_erreur($this, " <p><b>". $m[1] . "</b> "
259 . _T('zxml_nonvide_balise') // message a affiner
260 );
261 }
262 }
263 if ($f = $this->process['text']) $f($this, $data);
264 }
265
266 function piElement($phraseur, $target, $data)
267 {
268 if ($f = $this->process['pi']) $f($this, $target, $data);
269 }
270
271 // Denonciation des entitees XML inconnues
272 // Pour contourner le bug de conception de SAX qui ne signale pas si elles
273 // sont dans un attribut, les entites les plus frequentes ont ete
274 // transcodees au prealable (sauf & < > " que SAX traite correctement).
275 // On ne les verra donc pas passer a cette etape, contrairement a ce que
276 // le source de la page laisse legitimement supposer.
277
278 // http://doc.spip.org/@defautElement
279 function defaultElement($phraseur, $data)
280 {
281 if (!preg_match('/^<!--/', $data)
282 AND (preg_match_all('/&([^;]*)?/', $data, $r, PREG_SET_ORDER)))
283 foreach ($r as $m) {
284 list($t,$e) = $m;
285 if (!isset($this->dtc->entites[$e]))
286 coordonnees_erreur($this, " <b>$e</b> "
287 . _T('zxml_inconnu_entite')
288 . ' '
289 );
290 }
291 if (isset($this->process['default']) AND ($f = $this->process['default'])) {
292 $f($this, $data);
293 }
294 }
295
296 // http://doc.spip.org/@phraserTout
297 function phraserTout($phraseur, $data)
298 {
299 xml_parsestring($this, $data);
300
301 if (!$this->dtc OR preg_match(',^' . _MESSAGE_DOCTYPE . ',', $data)) {
302 $this->err[]= array('DOCTYPE ?', 0, 0);
303 } else {
304 $this->valider_passe2($this);
305 }
306 }
307
308 // Init
309 function ValidateurXML($process=array()) {
310 if (is_array($process)) $this->process = $process;
311 }
312
313 var $ids = array();
314 var $idrefs = array();
315 var $idrefss = array();
316 var $debuts = array();
317 var $fratrie = array();
318
319 var $dtc = NULL;
320 var $sax = NULL;
321 var $depth = "";
322 var $entete = '';
323 var $page = '';
324 var $res = "";
325 var $err = array();
326 var $contenu = array();
327 var $ouvrant = array();
328 var $reperes = array();
329 var $process = array(
330 'debut' => 'xml_debutElement',
331 'fin' => 'xml_finElement',
332 'text' => 'xml_textElement',
333 'pi' => 'xml_piElement',
334 'default' => 'xml_defaultElement'
335 );
336 }
337
338 // Retourne une structure ValidateurXML, dont le champ "err" est un tableau
339 // ayant comme entrees des sous-tableaux [message, ligne, colonne]
340
341 // http://doc.spip.org/@xml_valider_dist
342 function xml_valider_dist($page, $apply=false, $process=false, $doctype='', $charset=null)
343 {
344 $f = new ValidateurXML($process);
345 $sax = charger_fonction('sax', 'xml');
346 return $sax($page, $apply, $f, $doctype, $charset);
347 }
348 ?>