Je génère un document XML à partir d'un script PHP et je dois échapper les caractères spéciaux XML. Je connais la liste des caractères à échapper; mais quelle est la bonne façon de le faire ?
Faut-il échapper les caractères avec une barre oblique inverse (\ ') ou quelle est la bonne façon? Y a-t-il une fonction intégrée PHP qui peut gérer cela pour moi?
Utilisez les classes DOM pour générer l'intégralité de votre document XML. Il gérera les encodages et décodages dont nous ne voulons même pas nous soucier.
Edit: Cela a été critiqué par @Tchalvak:
L'objet DOM crée un document XML complet, il ne se prête pas facilement à l'encodage d'une chaîne seul.
Ce qui est faux, DOMDocument peut correctement sortir juste un fragment et non le document entier:
$doc->saveXML($fragment);
qui donne:
Test & <b> and encode </b> :)
Test &amp; <b> and encode </b> :)
un péché:
$doc = new DOMDocument();
$fragment = $doc->createDocumentFragment();
// adding XML verbatim:
$xml = "Test & <b> and encode </b> :)\n";
$fragment->appendXML($xml);
// adding text:
$text = $xml;
$fragment->appendChild($doc->createTextNode($text));
// output the result
echo $doc->saveXML($fragment);
Voir Démo
J'ai créé une fonction simple qui s'échappe avec les cinq "entités prédéfinies" qui sont en XML:
function xml_entities($string) {
return strtr(
$string,
array(
"<" => "<",
">" => ">",
'"' => """,
"'" => "'",
"&" => "&",
)
);
}
Exemple d'utilisation Démo :
$text = "Test & <b> and encode </b> :)";
echo xml_entities($text);
Sortie:
Test &amp; <b> and encode </b> :)
Un effet similaire peut être obtenu en utilisant str_replace
mais il est fragile à cause des doubles remplacements (non testé, déconseillé):
function xml_entities($string) {
return str_replace(
array("&", "<", ">", '"', "'"),
array("&", "<", ">", """, "'"),
$string
);
}
Qu'en est-il de la fonction htmlspecialchars()
?
htmlspecialchars($input, ENT_QUOTES | ENT_XML1, $encoding);
Remarque: le drapeau ENT_XML1
N'est disponible que si vous avez PHP 5.4.0 ou supérieur.
htmlspecialchars()
avec ces paramètres remplace les caractères suivants:
&
(Esperluette) devient &
"
(Guillemet double) devient "
'
(Guillemet simple) devient '
<
(Moins de) devient <
>
(Supérieur à) devient >
Vous pouvez obtenir la table de traduction en utilisant la fonction get_html_translation_table()
.
J'ai essayé de résoudre le problème d'entité XML, résolvez de cette façon:
htmlspecialchars($value, ENT_QUOTES, 'UTF-8')
Afin d'avoir un texte XML final valide, vous devez échapper à toutes les entités XML et avoir le texte écrit dans le même codage que l'instruction de traitement du document XML l'indique (le "codage" dans la ligne <?xml
) . Les caractères accentués n'ont pas besoin d'être échappés tant qu'ils sont encodés en tant que document.
Cependant, dans de nombreuses situations, simplement échapper l'entrée avec htmlspecialchars
peut conduire à des entités à double codage (par exemple é
Deviendrait &eacute;
), Donc je suggère de décoder les entités html d'abord:
function xml_escape($s)
{
$s = html_entity_decode($s, ENT_QUOTES, 'UTF-8');
$s = htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false);
return $s;
}
Vous devez maintenant vous assurer que tous les caractères accentués sont valides dans le codage du document XML. J'encourage fortement à toujours coder la sortie XML en UTF-8, car tous les analyseurs XML ne respectent pas l'encodage des instructions de traitement des documents XML. Si votre entrée peut provenir d'un jeu de caractères différent, essayez d'utiliser utf8_encode()
.
Il y a un cas spécial, votre entrée peut provenir de l'un de ces encodages: ISO-8859-1, ISO-8859-15, UTF-8, cp866, cp1251, cp1252 et KOI8-R - PHP les traite tous de la même façon, mais il y a de légères différences entre elles - dont certaines même iconv()
ne peuvent pas être gérées. Je n'ai pu résoudre ce problème d'encodage qu'en complétant utf8_encode()
comportement:
function encode_utf8($s)
{
$cp1252_map = array(
"\xc2\x80" => "\xe2\x82\xac",
"\xc2\x82" => "\xe2\x80\x9a",
"\xc2\x83" => "\xc6\x92",
"\xc2\x84" => "\xe2\x80\x9e",
"\xc2\x85" => "\xe2\x80\xa6",
"\xc2\x86" => "\xe2\x80\xa0",
"\xc2\x87" => "\xe2\x80\xa1",
"\xc2\x88" => "\xcb\x86",
"\xc2\x89" => "\xe2\x80\xb0",
"\xc2\x8a" => "\xc5\xa0",
"\xc2\x8b" => "\xe2\x80\xb9",
"\xc2\x8c" => "\xc5\x92",
"\xc2\x8e" => "\xc5\xbd",
"\xc2\x91" => "\xe2\x80\x98",
"\xc2\x92" => "\xe2\x80\x99",
"\xc2\x93" => "\xe2\x80\x9c",
"\xc2\x94" => "\xe2\x80\x9d",
"\xc2\x95" => "\xe2\x80\xa2",
"\xc2\x96" => "\xe2\x80\x93",
"\xc2\x97" => "\xe2\x80\x94",
"\xc2\x98" => "\xcb\x9c",
"\xc2\x99" => "\xe2\x84\xa2",
"\xc2\x9a" => "\xc5\xa1",
"\xc2\x9b" => "\xe2\x80\xba",
"\xc2\x9c" => "\xc5\x93",
"\xc2\x9e" => "\xc5\xbe",
"\xc2\x9f" => "\xc5\xb8"
);
$s=strtr(utf8_encode($s), $cp1252_map);
return $s;
}
Si vous avez besoin d'une sortie xml appropriée, simplexml est le chemin à parcourir:
Un échappement correct est le moyen d'obtenir une sortie XML correcte, mais vous devez gérer l'échappement différemment pour les attributs et éléments . (C'est la réponse de Tomas est incorrecte).
J'ai écrit/volé quelques code Java il y a quelque temps qui différencie l'attribut et l'échappement de l'élément. La raison en est que l'analyseur XML considère tous les espaces blancs comme particuliers dans les attributs.
Il devrait être trivial de le porter sur PHP (vous pouvez utiliser l'approche de Tomas Jancik avec l'échappement approprié ci-dessus). Vous n'avez pas à vous soucier de l'échappement des entités étendues si vous utilisez UTF-8
.
Si vous ne voulez pas porter mon Java vous pouvez regarder XMLWriter qui est basé sur le flux et utilise libxml, il devrait donc être très efficace.
Vous pouvez utiliser ces méthodes: http://php.net/manual/en/function.htmlentities.php
De cette façon, toutes les entités (html/xml) sont échappées et vous pouvez placer votre chaîne dans des balises XML