PageRenderTime 50ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/ecrire/xml/sax.php

https://github.com/denisbz/SPIP
PHP | 292 lines | 274 code | 4 blank | 14 comment | 2 complexity | 69e566334a0b9545ba31b944e4a0f497 MD5 | raw file
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2011 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. if (!defined('_ECRIRE_INC_VERSION')) return;
  12. include_spip('inc/charsets');
  13. include_spip('xml/interfaces');
  14. /**
  15. * Encoder les entites
  16. * @param string $texte
  17. * @return string
  18. */
  19. function xml_entites_html($texte){
  20. if (!is_string($texte) OR !$texte
  21. OR strpbrk($texte, "&\"'<>")==false
  22. ) return $texte;
  23. $texte = htmlspecialchars($texte,ENT_QUOTES);
  24. return $texte;
  25. }
  26. // http://doc.spip.org/@xml_debutElement
  27. function xml_debutElement($phraseur, $name, $attrs)
  28. {
  29. $depth = $phraseur->depth;
  30. $t = isset($phraseur->ouvrant[$depth]) ? $phraseur->ouvrant[$depth] : ' ';
  31. // espace initial signifie: deja integree au resultat
  32. if ($t[0] != ' ')
  33. {
  34. $phraseur->res .= '<' . $t . '>';
  35. $phraseur->ouvrant[$depth] = ' ' . $t;
  36. }
  37. $t = $phraseur->contenu[$depth];
  38. // n'indenter que s'il y a un separateur avant
  39. $phraseur->res .= preg_replace("/[\n\t ]+$/", "\n$depth", $t);
  40. $phraseur->contenu[$depth] = "";
  41. $att = '';
  42. $sep = ' ';
  43. foreach ($attrs as $k => $v) {
  44. $delim = strpos($v, "'") === false ? "'" : '"';
  45. $val = xml_entites_html($v);
  46. $att .= $sep . $k . "=" . $delim
  47. . ($delim !== '"' ? str_replace('&quot;', '"', $val) : $val)
  48. . $delim;
  49. $sep = "\n $depth";
  50. }
  51. $phraseur->depth .= ' ';
  52. $phraseur->contenu[$phraseur->depth] = "";
  53. $phraseur->ouvrant[$phraseur->depth] = $name . $att;
  54. $phraseur->reperes[$phraseur->depth] = xml_get_current_line_number($phraseur->sax);
  55. }
  56. // http://doc.spip.org/@xml_finElement
  57. function xml_finElement($phraseur, $name, $fusion_bal=false)
  58. {
  59. $ouv = $phraseur->ouvrant[$phraseur->depth];
  60. if ($ouv[0] != ' ')
  61. $phraseur->ouvrant[$phraseur->depth] = ' ' . $ouv;
  62. else $ouv= "";
  63. $t = $phraseur->contenu[$phraseur->depth];
  64. $phraseur->depth = substr($phraseur->depth, 2);
  65. $t = preg_replace("/[\n\t ]+$/", "\n" . $phraseur->depth, $t);
  66. // fusion <balise></balise> en <balise />.
  67. // ATTENTION, certains clients http croient que fusion ==> pas d'atttributs
  68. // en particulier pour les balises Script et A.
  69. // en presence d'attributs ne le faire que si la DTD est dispo et d'accord
  70. // (param fusion_bal)
  71. if ($t || (($ouv != $name) AND !$fusion_bal))
  72. $phraseur->res .= ($ouv ? ('<' . $ouv . '>') : '') . $t . "</" . $name . ">";
  73. else
  74. $phraseur->res .= ($ouv ? ('<' . $ouv . ' />') : ("</" . $name . ">"));
  75. }
  76. // http://doc.spip.org/@xml_textElement
  77. function xml_textElement($phraseur, $data)
  78. {
  79. $depth = $phraseur->depth;
  80. $phraseur->contenu[$depth] .= preg_match('/^script/',$phraseur->ouvrant[$depth])
  81. ? $data
  82. : xml_entites_html($data);
  83. }
  84. function xml_piElement($phraseur, $target, $data)
  85. {
  86. $depth = $phraseur->depth;
  87. if (strtolower($target) != "php")
  88. $phraseur->contenu[$depth] .= $data;
  89. else {
  90. ob_start();
  91. eval($data);
  92. $data = ob_get_contents();
  93. ob_end_clean();
  94. $phraseur->contenu[$depth] .= $data;
  95. }
  96. }
  97. // http://doc.spip.org/@xml_defautElement
  98. function xml_defaultElement($phraseur, $data)
  99. {
  100. $depth = $phraseur->depth;
  101. if (!isset($phraseur->contenu[$depth])) $phraseur->contenu[$depth]='';
  102. $phraseur->contenu[$depth] .= $data;
  103. }
  104. // http://doc.spip.org/@xml_parsestring
  105. function xml_parsestring($phraseur, $data)
  106. {
  107. $phraseur->contenu[$phraseur->depth] ='';
  108. if (!xml_parse($phraseur->sax, $data, true)) {
  109. coordonnees_erreur($phraseur,
  110. xml_error_string(xml_get_error_code($phraseur->sax))
  111. . "<br />\n" .
  112. (!$phraseur->depth ? '' :
  113. ('(' .
  114. _T('erreur_balise_non_fermee') .
  115. " <tt>" .
  116. $phraseur->ouvrant[$phraseur->depth] .
  117. "</tt> " .
  118. _T('ligne') .
  119. " " .
  120. $phraseur->reperes[$phraseur->depth] .
  121. ") <br />\n" )));
  122. }
  123. }
  124. // http://doc.spip.org/@coordonnees_erreur
  125. function coordonnees_erreur($phraseur, $msg)
  126. {
  127. $entete_length = substr_count($phraseur->entete,"\n");
  128. $phraseur->err[] = array($msg,
  129. xml_get_current_line_number($phraseur->sax) + $entete_length,
  130. xml_get_current_column_number($phraseur->sax));
  131. }
  132. // http://doc.spip.org/@xml_sax_dist
  133. function xml_sax_dist($page, $apply=false, $phraseur=NULL, $doctype='')
  134. {
  135. if ($apply) {
  136. ob_start();
  137. if (is_array($apply))
  138. $r = call_user_func_array($page, $apply);
  139. else $r = $page();
  140. $page = ob_get_contents();
  141. ob_end_clean();
  142. // fonction sans aucun "echo", ca doit etre le resultat
  143. if (!$page) $page = $r;
  144. }
  145. if (!$page) return '';
  146. // charger la DTD et transcoder les entites,
  147. // et escamoter le doctype que sax mange en php5 mais pas en php4
  148. if (!$doctype) {
  149. if (!$r = analyser_doctype($page)) {
  150. $page = _MESSAGE_DOCTYPE . _DOCTYPE_ECRIRE
  151. . preg_replace(_REGEXP_DOCTYPE, '', $page);
  152. $r = analyser_doctype($page);
  153. }
  154. list($entete, $avail, $grammaire, $rotlvl) = $r;
  155. $page = substr($page,strlen($entete));
  156. } else {
  157. $avail = 'SYSTEM';
  158. $grammaire = $doctype;
  159. $rotlvl = basename($grammaire);
  160. }
  161. include_spip('xml/analyser_dtd');
  162. $dtc = charger_dtd($grammaire, $avail, $rotlvl);
  163. $page = sax_bug($page, $dtc);
  164. // compatibilite Tidy espace public
  165. if (!$phraseur) {
  166. $indenter_xml = charger_fonction('indenter', 'xml');
  167. return $indenter_xml($page, $apply);
  168. }
  169. $xml_parser = xml_parser_create($GLOBALS['meta']['charset']);
  170. xml_set_element_handler($xml_parser,
  171. array($phraseur, "debutElement"),
  172. array($phraseur, "finElement"));
  173. xml_set_character_data_handler($xml_parser,
  174. array($phraseur, "textElement"));
  175. xml_set_processing_instruction_handler($xml_parser,
  176. array($phraseur, 'piElement'));
  177. xml_set_default_handler($xml_parser,
  178. array($phraseur, "defaultElement"));
  179. xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
  180. $phraseur->sax = $xml_parser;
  181. $phraseur->entete = $entete;
  182. $phraseur->page = $page;
  183. $phraseur->dtc = $dtc;
  184. $phraseur->phraserTout($xml_parser, $page);
  185. xml_parser_free($xml_parser);
  186. $phraseur->sax = '';
  187. return $phraseur;
  188. }
  189. // SAX ne dit pas si une Entite est dans un attribut ou non.
  190. // Les eliminer toutes sinon celles des attributs apparaissent en zone texte!
  191. // Celles fondamentales pour la lecture (lt gt quot amp) sont conservees
  192. // (d'ailleurs SAX ne les considere pas comme des entites dans un attribut)
  193. // Si la DTD est dispo, on va chercher les entites dedans
  194. // sinon on se rabat sur ce qu'en connait SPIP en standard.
  195. // http://doc.spip.org/@sax_bug
  196. function sax_bug($data, $dtc)
  197. {
  198. if ($dtc) {
  199. $trans = array();
  200. foreach($dtc->entites as $k => $v) {
  201. if (!strpos(" amp lt gt quot ", $k))
  202. $trans["&$k;"] = $v;
  203. }
  204. $data = strtr($data, $trans);
  205. } else {
  206. $data = html2unicode($data, true);
  207. }
  208. return unicode2charset($data);
  209. }
  210. // Retirer < ? xml... ? > et autre PI, ainsi que les commentaires en debut
  211. // afin de reperer le Doctype et le decomposer selon:
  212. // http://www.freebsd.org/doc/fr_FR.ISO8859-1/books/fdp-primer/sgml-primer-doctype-declaration.html
  213. // Si pas de Doctype et premiere balise = RSS prendre la doctype RSS 0.91:
  214. // les autres formats RSS n'ont pas de DTD,
  215. // mais un XML Schema que SPIP ne fait pas encore lire.
  216. // http://doc.spip.org/@analyser_doctype
  217. function analyser_doctype($data)
  218. {
  219. if (!preg_match(_REGEXP_DOCTYPE, $data, $page)) {
  220. if (preg_match(_REGEXP_XML, $data, $page)) {
  221. list(,$entete, $topelement) = $page;
  222. if ($topelement == 'rss')
  223. return array($entete, 'PUBLIC',
  224. _DOCTYPE_RSS,
  225. 'rss-0.91.dtd');
  226. else {
  227. $dtd = $topelement . '.dtd';
  228. $f = find_in_path($dtd);
  229. if (file_exists($f))
  230. return array($entete, 'SYSTEM', $f, $dtd);
  231. }
  232. }
  233. spip_log("Dtd pas vu pour " . substr($data, 0, 100));
  234. return array();
  235. }
  236. list($entete,, $topelement, $avail,$suite) = $page;
  237. if (!preg_match('/^"([^"]*)"\s*(.*)$/', $suite, $r))
  238. if (!preg_match("/^'([^']*)'\s*(.*)$/", $suite, $r))
  239. return array();
  240. list(,$rotlvl, $suite) = $r;
  241. if (!$suite) {
  242. if ($avail != 'SYSTEM') return array();
  243. $grammaire = $rotlvl;
  244. $rotlvl = '';
  245. } else {
  246. if (!preg_match('/^"([^"]*)"\s*$/', $suite, $r))
  247. if (!preg_match("/^'([^']*)'\s*$/", $suite, $r))
  248. return array();
  249. $grammaire = $r[1];
  250. }
  251. return array($entete, $avail, $grammaire, $rotlvl);
  252. }
  253. ?>