3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2012 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
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 \***************************************************************************/
13 if (!defined('_ECRIRE_INC_VERSION')) return;
15 include_spip('inc/charsets');
16 include_spip('xml/interfaces');
20 * @param string $texte
23 function xml_entites_html($texte){
24 if (!is_string($texte) OR !$texte
25 OR strpbrk($texte, "&\"'<>")==false
28 $texte = htmlspecialchars($texte,ENT_QUOTES
);
32 // http://doc.spip.org/@xml_debutElement
33 function xml_debutElement($phraseur, $name, $attrs)
35 $depth = $phraseur->depth
;
37 $t = isset($phraseur->ouvrant
[$depth]) ?
$phraseur->ouvrant
[$depth] : ' ';
38 // espace initial signifie: deja integree au resultat
41 $phraseur->res
.= '<' . $t . '>';
42 $phraseur->ouvrant
[$depth] = ' ' . $t;
44 $t = $phraseur->contenu
[$depth];
45 // n'indenter que s'il y a un separateur avant
46 $phraseur->res
.= preg_replace("/[\n\t ]+$/", "\n$depth", $t);
47 $phraseur->contenu
[$depth] = "";
50 foreach ($attrs as $k => $v) {
51 $delim = strpos($v, "'") === false ?
"'" : '"';
52 $val = xml_entites_html($v);
53 $att .= $sep . $k . "=" . $delim
54 . ($delim !== '"' ?
str_replace('"', '"', $val) : $val)
58 $phraseur->depth
.= ' ';
59 $phraseur->contenu
[$phraseur->depth
] = "";
60 $phraseur->ouvrant
[$phraseur->depth
] = $name . $att;
61 $phraseur->reperes
[$phraseur->depth
] = xml_get_current_line_number($phraseur->sax
);
64 // http://doc.spip.org/@xml_finElement
65 function xml_finElement($phraseur, $name, $fusion_bal=false)
67 $ouv = $phraseur->ouvrant
[$phraseur->depth
];
70 $phraseur->ouvrant
[$phraseur->depth
] = ' ' . $ouv;
72 $t = $phraseur->contenu
[$phraseur->depth
];
73 $phraseur->depth
= substr($phraseur->depth
, 2);
74 $t = preg_replace("/[\n\t ]+$/", "\n" . $phraseur->depth
, $t);
76 // fusion <balise></balise> en <balise />.
77 // ATTENTION, certains clients http croient que fusion ==> pas d'atttributs
78 // en particulier pour les balises Script et A.
79 // en presence d'attributs ne le faire que si la DTD est dispo et d'accord
82 if ($t ||
(($ouv != $name) AND !$fusion_bal))
83 $phraseur->res
.= ($ouv ?
('<' . $ouv . '>') : '') . $t . "</" . $name . ">";
85 $phraseur->res
.= ($ouv ?
('<' . $ouv . ' />') : ("</" . $name . ">"));
88 // http://doc.spip.org/@xml_textElement
89 function xml_textElement($phraseur, $data)
91 $depth = $phraseur->depth
;
92 $phraseur->contenu
[$depth] .= preg_match('/^script/',$phraseur->ouvrant
[$depth])
94 : xml_entites_html($data);
97 function xml_piElement($phraseur, $target, $data)
99 $depth = $phraseur->depth
;
101 if (strtolower($target) != "php")
102 $phraseur->contenu
[$depth] .= $data;
106 $data = ob_get_contents();
108 $phraseur->contenu
[$depth] .= $data;
113 // http://doc.spip.org/@xml_defautElement
114 function xml_defaultElement($phraseur, $data)
116 $depth = $phraseur->depth
;
118 if (!isset($phraseur->contenu
[$depth])) $phraseur->contenu
[$depth]='';
119 $phraseur->contenu
[$depth] .= $data;
122 // http://doc.spip.org/@xml_parsestring
123 function xml_parsestring($phraseur, $data)
125 $phraseur->contenu
[$phraseur->depth
] ='';
127 if (!xml_parse($phraseur->sax
, $data, true)) {
128 coordonnees_erreur($phraseur,
129 xml_error_string(xml_get_error_code($phraseur->sax
))
131 (!$phraseur->depth ?
'' :
133 _T('erreur_balise_non_fermee') .
135 $phraseur->ouvrant
[$phraseur->depth
] .
139 $phraseur->reperes
[$phraseur->depth
] .
144 // http://doc.spip.org/@coordonnees_erreur
145 function coordonnees_erreur($phraseur, $msg)
147 $entete_length = substr_count($phraseur->entete
,"\n");
148 $phraseur->err
[] = array($msg,
149 xml_get_current_line_number($phraseur->sax
) +
$entete_length,
150 xml_get_current_column_number($phraseur->sax
));
153 // http://doc.spip.org/@xml_sax_dist
154 function xml_sax_dist($page, $apply=false, $phraseur=NULL, $doctype='', $charset=null)
156 if (is_null($charset))
157 $charset = $GLOBALS['meta']['charset'];
160 if (is_array($apply))
161 $r = call_user_func_array($page, $apply);
163 $page = ob_get_contents();
165 // fonction sans aucun "echo", ca doit etre le resultat
166 if (!$page) $page = $r;
169 if (!$page) return '';
170 // charger la DTD et transcoder les entites,
171 // et escamoter le doctype que sax mange en php5 mais pas en php4
173 if (!$r = analyser_doctype($page)) {
174 $page = _MESSAGE_DOCTYPE
. _DOCTYPE_ECRIRE
175 . preg_replace(_REGEXP_DOCTYPE
, '', $page);
176 $r = analyser_doctype($page);
178 list($entete, $avail, $grammaire, $rotlvl) = array_pad($r, 4, null);
179 $page = substr($page,strlen($entete));
182 $grammaire = $doctype;
183 $rotlvl = basename($grammaire);
186 include_spip('xml/analyser_dtd');
187 $dtc = charger_dtd($grammaire, $avail, $rotlvl);
188 $page = sax_bug($page, $dtc, $charset);
190 // compatibilite Tidy espace public
192 $indenter_xml = charger_fonction('indenter', 'xml');
193 return $indenter_xml($page, $apply);
196 $xml_parser = xml_parser_create($charset);
198 xml_set_element_handler($xml_parser,
199 array($phraseur, "debutElement"),
200 array($phraseur, "finElement"));
202 xml_set_character_data_handler($xml_parser,
203 array($phraseur, "textElement"));
205 xml_set_processing_instruction_handler($xml_parser,
206 array($phraseur, 'piElement'));
208 xml_set_default_handler($xml_parser,
209 array($phraseur, "defaultElement"));
211 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING
, false);
213 $phraseur->sax
= $xml_parser;
214 if (isset($entete)) {
215 $phraseur->entete
= $entete;
217 $phraseur->page
= $page;
218 $phraseur->dtc
= $dtc;
219 $phraseur->phraserTout($xml_parser, $page);
220 xml_parser_free($xml_parser);
225 // SAX ne dit pas si une Entite est dans un attribut ou non.
226 // Les eliminer toutes sinon celles des attributs apparaissent en zone texte!
227 // Celles fondamentales pour la lecture (lt gt quot amp) sont conservees
228 // (d'ailleurs SAX ne les considere pas comme des entites dans un attribut)
229 // Si la DTD est dispo, on va chercher les entites dedans
230 // sinon on se rabat sur ce qu'en connait SPIP en standard.
232 // http://doc.spip.org/@sax_bug
233 function sax_bug($data, $dtc, $charset=null)
235 if (is_null($charset))
236 $charset = $GLOBALS['meta']['charset'];
241 foreach($dtc->entites
as $k => $v) {
242 if (!strpos(" amp lt gt quot ", $k))
245 $data = strtr($data, $trans);
247 $data = html2unicode($data, true);
249 return unicode2charset($data, $charset);
252 // Retirer < ? xml... ? > et autre PI, ainsi que les commentaires en debut
253 // afin de reperer le Doctype et le decomposer selon:
254 // http://www.freebsd.org/doc/fr_FR.ISO8859-1/books/fdp-primer/sgml-primer-doctype-declaration.html
255 // Si pas de Doctype et premiere balise = RSS prendre la doctype RSS 0.91:
256 // les autres formats RSS n'ont pas de DTD,
257 // mais un XML Schema que SPIP ne fait pas encore lire.
258 // http://doc.spip.org/@analyser_doctype
259 function analyser_doctype($data)
261 if (!preg_match(_REGEXP_DOCTYPE
, $data, $page)) {
262 if (preg_match(_REGEXP_XML
, $data, $page)) {
263 list(,$entete, $topelement) = $page;
264 if ($topelement == 'rss')
265 return array($entete, 'PUBLIC',
269 $dtd = $topelement . '.dtd';
270 $f = find_in_path($dtd);
272 return array($entete, 'SYSTEM', $f, $dtd);
275 spip_log("Dtd pas vu pour " . substr($data, 0, 100));
278 list($entete,, $topelement, $avail,$suite) = $page;
280 if (!preg_match('/^"([^"]*)"\s*(.*)$/', $suite, $r))
281 if (!preg_match("/^'([^']*)'\s*(.*)$/", $suite, $r))
283 list(,$rotlvl, $suite) = $r;
286 if ($avail != 'SYSTEM') return array();
287 $grammaire = $rotlvl;
290 if (!preg_match('/^"([^"]*)"\s*$/', $suite, $r))
291 if (!preg_match("/^'([^']*)'\s*$/", $suite, $r))
297 return array($entete, $avail, $grammaire, $rotlvl);