3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2019 *
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 \***************************************************************************/
14 * Outils pour lecture de XML
16 * @package SPIP\Core\XML
19 if (!defined('_ECRIRE_INC_VERSION')) {
25 * Lit un fichier xml donné et renvoie son arbre.
29 * include_spip('inc/xml');
30 * $arbre = spip_xml_load(_DIR_PLUGINS . "$plug/plugin.xml");
33 * @uses spip_xml_parse()
35 * @param string $fichier
36 * Chemin local ou URL distante du fichier XML
38 * true pour râler si une balise n'est pas correctement fermée, false sinon.
39 * @param bool $clean ?
40 * @param int $taille_max
41 * Taille maximale si fichier distant
42 * @param string|array $datas
43 * Données à envoyer pour récupérer le fichier distant
44 * @param int $profondeur ?
46 * - array : l'arbre XML,
47 * - false si l'arbre xml ne peut être créé ou est vide
49 function spip_xml_load($fichier, $strict = true, $clean = true, $taille_max = 1048576, $datas = '', $profondeur = -1) {
51 if (tester_url_absolue($fichier)) {
52 include_spip('inc/distant');
53 $contenu = recuperer_page($fichier, false, false, $taille_max, $datas);
55 lire_fichier($fichier, $contenu);
59 $arbre = spip_xml_parse($contenu, $strict, $clean, $profondeur);
62 return count($arbre) ?
$arbre : false;
65 if (!defined('_SPIP_XML_TAG_SPLIT')) {
66 define('_SPIP_XML_TAG_SPLIT', "{<([^:>][^>]*?)>}sS");
70 * Parse une chaine XML donnée et retourne un tableau.
72 * @see spip_xml_aplatit() pour l'inverse
74 * @param string $texte
77 * true pour râler si une balise n'est pas correctement fermée, false sinon.
78 * @param bool $clean ?
79 * @param int $profondeur ?
81 * - array : l'arbre XML,
82 * - false si l'arbre xml ne peut être créé ou est vide
84 function spip_xml_parse(&$texte, $strict = true, $clean = true, $profondeur = -1) {
86 // enlever les commentaires
88 if ($clean === true) {
89 if (preg_match(",<\?xml\s(.*?)encoding=['\"]?(.*?)['\"]?(\s(.*))?\?>,im", $texte, $regs)) {
92 $texte = preg_replace(',<!--(.*?)-->,is', '', $texte);
93 $texte = preg_replace(',<\?(.*?)\?>,is', '', $texte);
94 include_spip('inc/charsets');
96 //$texte = importer_charset($texte,$charset);
98 if (is_string($clean)) {
103 // tant qu'il y a des tags
104 $chars = preg_split(_SPIP_XML_TAG_SPLIT
, $txt, 2, PREG_SPLIT_DELIM_CAPTURE
);
105 while (count($chars) >= 2) {
107 //$chars = preg_split("{<([^>]*?)>}s",$txt,2,PREG_SPLIT_DELIM_CAPTURE);
109 // $before doit etre vide ou des espaces uniquements!
110 $before = trim($chars[0]);
112 if (strlen($before) > 0) {
113 return importer_charset($texte, $charset);
114 }//$texte; // before non vide, donc on est dans du texte
116 $tag = rtrim($chars[1]);
119 if (strncmp($tag, '![CDATA[', 8) == 0) {
120 return importer_charset($texte, $charset);
122 if (substr($tag, -1) == '/') { // self closing tag
123 $tag = rtrim(substr($tag, 0, strlen($tag) - 1));
126 $closing_tag = preg_split(",\s|\t|\n|\r,", trim($tag));
127 $closing_tag = reset($closing_tag);
129 $ncclos = strlen("</$closing_tag>");
130 $p = strpos($txt, "</$closing_tag>");
131 if ($p !== false and (strpos($txt, "<") < $p)) {
137 and ($morceau = substr($txt, $d, $p - $d))
138 and (($nopen +
= preg_match_all("{<" . preg_quote($closing_tag) . "(\s*>|\s[^>]*[^/>]>)}is", $morceau,
139 $matches, PREG_SET_ORDER
)) > $nclose)
143 $p = strpos($txt, "</$closing_tag>", $d);
148 $out[$tag][] = "erreur : tag fermant $tag manquant::$txt";
152 return importer_charset($texte, $charset);
153 }//$texte // un tag qui constitue du texte a reporter dans $before
155 $content = substr($txt, 0, $p);
156 $txt = substr($txt, $p +
$ncclos);
157 if ($profondeur == 0 or strpos($content, "<") === false) // eviter une recursion si pas utile
159 $out[$tag][] = importer_charset($content, $charset);
162 $out[$tag][] = spip_xml_parse($content, $strict, $clean, $profondeur - 1);
165 $chars = preg_split(_SPIP_XML_TAG_SPLIT
, $txt, 2, PREG_SPLIT_DELIM_CAPTURE
);
167 if (count($out) && (strlen(trim($txt)) == 0)) {
170 return importer_charset($texte, $charset);
174 // http://code.spip.net/@spip_xml_aplatit
175 function spip_xml_aplatit($arbre, $separateur = " ") {
177 if (is_array($arbre)) {
178 foreach ($arbre as $tag => $feuille) {
179 if (is_array($feuille)) {
180 if ($tag !== intval($tag)) {
181 $f = spip_xml_aplatit($feuille, $separateur);
183 $tagf = explode(" ", $tag);
185 $s .= "<$tag>$f</$tagf>";
190 $s .= spip_xml_aplatit($feuille);
194 $s .= "$feuille$separateur";
199 return strlen($separateur) ?
substr($s, 0, -strlen($separateur)) : $s;
202 // http://code.spip.net/@spip_xml_tagname
203 function spip_xml_tagname($tag) {
204 if (preg_match(',^([a-z][\w:]*),i', $tag, $reg)) {
211 // http://code.spip.net/@spip_xml_decompose_tag
212 function spip_xml_decompose_tag($tag) {
213 $tagname = spip_xml_tagname($tag);
215 $tag = ltrim(strpbrk($tag, " \n\t"));
216 $p = strpos($tag, '=');
217 while ($p !== false) {
218 $attr = trim(substr($tag, 0, $p));
219 $tag = ltrim(substr($tag, $p +
1));
221 $p = strpos($tag, $quote, 1);
222 $cont = substr($tag, 1, $p - 1);
223 $liste[$attr] = $cont;
224 $tag = substr($tag, $p +
1);
225 $p = strpos($tag, '=');
228 return array($tagname, $liste);
232 * Recherche dans un arbre XML généré par `spip_xml_parse()` (ou une branche de cet arbre)
233 * les clés de l'arbre qui valident la regexp donnée.
235 * Les branches qui valident la regexp sont retournées dans le tableau `$matches`.
237 * @see spip_xml_parse()
238 * @see spip_xml_decompose_tag()
240 * @param string $regexp
241 * Expression régulière
242 * @param array $arbre
244 * @param array $matches
245 * Branches de l'arbre validant la rexgep
246 * @param bool $init ?
248 * false si aucun élément ne valide l'expression régulière, true sinon.
250 function spip_xml_match_nodes($regexp, &$arbre, &$matches, $init = true) {
254 if (is_array($arbre) && count($arbre)) {
255 foreach (array_keys($arbre) as $tag) {
256 if (preg_match($regexp, $tag)) {
257 $matches[$tag] = &$arbre[$tag];
259 if (is_array($arbre[$tag])) {
260 foreach (array_keys($arbre[$tag]) as $occurences) {
261 spip_xml_match_nodes($regexp, $arbre[$tag][$occurences], $matches, false);
267 return (count($matches));