在PHP中生成XML文档(转义字符)

我从PHP脚本生成一个XML文档,我需要转义XML特殊字符。 我知道应该逃跑的人物名单, 但是做到这一点的正确方法是什么?

字符是否应该用反斜杠(\)或者正确的方式逃脱? 有没有任何内置的PHP函数可以为我处理?

使用DOM类来生成整个XML文档。 它将处理我们甚至不想关心的编码和解码。


编辑:这被批评@Tchalvak:

DOM对象创build了一个完整的XML文档,它不容易自己编写一个string。

哪个是错误的,DOMDocument可以正确输出一个片段而不是整个文档:

$doc->saveXML($fragment); 

这使:

 Test &amp; <b> and encode </b> :) Test &amp;amp; &lt;b&gt; and encode &lt;/b&gt; :) 

如下所示:

 $doc = new DOMDocument(); $fragment = $doc->createDocumentFragment(); // adding XML verbatim: $xml = "Test &amp; <b> and encode </b> :)\n"; $fragment->appendXML($xml); // adding text: $text = $xml; $fragment->appendChild($doc->createTextNode($text)); // output the result echo $doc->saveXML($fragment); 

看演示

我创build了一个简单的函数,使用XML中的五个“预定义实体”进行转义:

 function xml_entities($string) { return strtr( $string, array( "<" => "&lt;", ">" => "&gt;", '"' => "&quot;", "'" => "&apos;", "&" => "&amp;", ) ); } 

用法示例演示 :

 $text = "Test &amp; <b> and encode </b> :)"; echo xml_entities($text); 

输出:

 Test &amp;amp; &lt;b&gt; and encode &lt;/b&gt; :) 

使用str_replace可以达到类似的效果,但是由于双重replace(未经testing,不推荐),它是脆弱的:

 function xml_entities($string) { return str_replace( array("&", "<", ">", '"', "'"), array("&amp;", "&lt;", "&gt;", "&quot;", "&apos;"), $string ); } 

那么htmlspecialchars()函数呢?

 htmlspecialchars($input, ENT_QUOTES | ENT_XML1, $encoding); 

注意:只有PHP 5.4.0或更高版本才能使用ENT_XML1标志。

带有这些参数的htmlspecialchars()replace下列字符:

  • & (&符号)变成&amp;
  • " (双引号)变成&quot;
  • (单引号)变成&apos;
  • < (小于)成为&lt;
  • > (大于)变成&gt;

您可以使用get_html_translation_table()函数获取翻译表。

试图很难处理XML实体问题,这样解决:

 htmlspecialchars($value, ENT_QUOTES, 'UTF-8') 

为了有一个有效的最终XML文本,您需要转义所有XML实体,并使用与XML文档处理指令相同的编码(“ <?xml行中的“encoding”)编写文本。 重音字符只要被编码为文档就不需要转义。

然而,在许多情况下,使用htmlspecialchars简单地转义input可能会导致双重编码的实体(例如&eacute;将变成&amp;eacute; ),所以我build议先解码html实体:

 function xml_escape($s) { $s = html_entity_decode($s, ENT_QUOTES, 'UTF-8'); $s = htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false); return $s; } 

现在您需要确保所有重音字符在XML文档编码中都是有效的。 我强烈build议始终使用UTF-8对XML输出进行编码,因为并非所有XMLparsing器都遵守XML文档处理指令编码。 如果您的input可能来自不同的字符集,请尝试使用utf8_encode()

有一个特殊情况,这是你的input可能来自这些编码之一:ISO-8859-1,ISO-8859-15,UTF-8,cp866,cp1251,cp1252和KOI8-R-PHP将它们全部视为相同,但是它们之间有一些细微的差别 – 其中一些甚至iconv()不能处理。 我只能通过补充utf8_encode()行为来解决这个编码问题:

 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; } 

如果你需要正确的XML输出,simplexml是要走的路:

http://www.php.net/manual/en/simplexmlelement.asxml.php

适当的转义是获得正确的XML输出的方式,但是您需要针对属性元素处理不同的转义。 (这是Tomas的答案是不正确的)。

我写了/偷了一些Java代码 ,区分属性和元素转义。 原因是XMLparsing器认为所有的空白在属性中都是特别的。

把它移植到PHP上应该是微不足道的(你可以使用Tomas Jancik的方法进行上面的转义)。 如果您使用UTF-8则不必担心转义扩展实体。

如果你不想移植我的Java代码,你可以看看基于stream的XMLWriter ,并使用libxml,所以它应该是非常有效的。

你可以使用这个方法: http : //php.net/manual/en/function.htmlentities.php

这样,所有的实体(html / xml)都会被转义,您可以将您的string放入XML标签中

  function replace_char($arr1) { $arr[]=preg_replace('>','&gt', $arr1); $arr[]=preg_replace('<','&lt', $arr1); $arr[]=preg_replace('"','&quot', $arr1); $arr[]=preg_replace('\'','&apos', $arr1); $arr[]=preg_replace('&','&amp', $arr1); return $arr; } 

基于sadeghj的解决scheme,下面的代码为我工作:

 /** * @param $arr1 the single string that shall be masked * @return the resulting string with the masked characters */ function replace_char($arr1) { if (strpos ($arr1,'&')!== FALSE) { //test if the character appears $arr1=preg_replace('/&/','&amp;', $arr1); // do this first } // just encode the if (strpos ($arr1,'>')!== FALSE) { $arr1=preg_replace('/>/','&gt;', $arr1); } if (strpos ($arr1,'<')!== FALSE) { $arr1=preg_replace('/</','&lt;', $arr1); } if (strpos ($arr1,'"')!== FALSE) { $arr1=preg_replace('/"/','&quot;', $arr1); } if (strpos ($arr1,'\'')!== FALSE) { $arr1=preg_replace('/\'/','&apos;', $arr1); } return $arr1; }