2010-10-18 107 views
45

我从PHP脚本生成XML文档,我需要转义XML特殊字符。 我知道应该逃避的字符列表;但是做到这一点的正确方法是什么?在PHP中生成XML文档(转义字符)

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

+0

@Tchalvak:你错了很多你在你的赏金描述批评点。我试图用现有的答案使其可见,希望这是有帮助的。 – hakre 2013-02-16 07:53:38

+0

我没有在我的答案中建议只使用DOM API来进行字符串转义。我建议你使用该API生成整个XML文档。这是为了回应你在赏金描述中提到的问题。 – 2013-02-16 20:02:18

回答

33

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


编辑:这是批评@Tchalvak:

的DOM对象创建一个完整的XML文档,它不轻易出借自己刚刚编码它自己的字符串。

哪项是错误的,DOM文档可以正常输出只是一个片段,而不是整个文件:

$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); 

Demo

+5

根据https://bugs.php.net/bug.php?id=31191你特别想使用[createTextNode](http://www.php.net/manual/en/domdocument.createtextnode.php)函数以获得适当的自动转义。 – Jonathan 2012-01-03 14:43:23

+0

我认为@Tchalvak问题是,它不是基于流。这是使用DOM将创建一堆对象。正如我在我的[回复](http://stackoverflow.com/a/15010355/318174)中提到的,他可以使用XMLWriter或将我的Java代码移植到PHP中,以正确转义(Tomas Jancik方式不正确)。 – 2013-02-22 16:53:28

+0

Whoops,http://eval.in/10980是使用本机处理的'xmlentities()'函数的正确实现。可能有一些开销,但是,可能值得使用本机/面向未来功能的安心。 – Kzqai 2013-02-22 23:54:26

0

您可以使用此方法: http://php.net/manual/en/function.htmlentities.php

这样一来所有的实体(HTML/XML)被转义,并且你可以把你的字符串XML标签内

+5

这是一个糟糕的解决方案,因为HTML实体比XML实体更大,大多数XML解析器不会识别XML实体列表中不存在的许多HTML实体。 – 2012-05-02 10:52:04

35

我创建简单的函数,与five "predefined entities"是在XML逸出:

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

用法例子Demo

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

输出:

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

类似的效果,可以实现通过使用str_replace,但它是脆弱的,因为双重替换(未经测试,不推荐):

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

您需要将''&“'和'”&“'数组元素移至开头,否则所有其他创建的实体都将替换其&符号。另外,'strtr'解决方案似乎根本不起作用。 – Ryan 2011-11-05 20:47:23

+2

5个XML实体?我希望这是简单的... – NDM 2012-12-12 17:42:52

+1

对于OP回答他自己的问题,这里真的存在严重的缺陷。我试图编辑它的好处,但我不知道是否使用了原始代码;) - @Nicky De Maeyer:是的,XML中有五个预定义实体,我放置了一个链接。 – hakre 2013-02-18 17:28:08

12

努力地处理XML实体问题,解决了这种方式:

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

这只适用于以XML定义这些实体的情况,请参阅http://www.w3.org/TR/xml-entity-names/ – hakre 2013-02-16 08:03:07

16

怎么样htmlspecialchars()功能?

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

注:如果您对PHP 5.4.0或更高版本的ENT_XML1标志才可用。

htmlspecialchars()与这些参数替换下列字符:

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

您可以通过使用get_html_translation_table()功能得到转换表。

+1

对于兼容XML的编码,没有特别需要使用ENT_XML1 - 至少适用于PHP版本4.3.0至5.5.0alpha4。一个简单的'htmlspecialchars($ input,ENT_QUOTES,$ encoding)''也可以完成这项工作,如果你可以使用数字而不是命名实体。 – hakre 2013-02-16 07:59:49

5

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

然而,在许多情况下,简单地逃脱输入与htmlspecialchars可能会导致双编码实体(例如&eacute;将成为&amp;eacute;),所以我首先建议解码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文档编码中都是有效的。我强烈建议始终使用UTF-8编码XML输出,因为并非所有XML解析器都遵守XML文档处理指令编码。如果您的输入可能来自不同的字符集,请尝试使用utf8_encode()

有一种特殊情况,您的输入可能来自以下编码之一:ISO-8859-1,ISO-8859-15,UTF-8,cp866,cp1251,cp1252和KOI8-R - PHP对待他们都是一样的,但是他们之间有一些细微的差异 - 其中一些甚至不能处理iconv()

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

正确的转义得到正确的方式,我只能通过补充utf8_encode()行为来解决这个编码问题XML输出但您需要以不同方式处理转义对于属性元素。 (这是托马斯的回答不正确)。

我写/偷了一些​​一段时间后,区分属性和元素转义。原因在于XML解析器认为所有的空白空间都是特别特殊的。

将它移植到PHP应该很简单(可以使用Tomas Jancik的方法进行上述适当的转义)。如果您使用UTF-8,则不必担心转义扩展实体。

如果您不想移植我的Java代码,您可以查看基于流的XMLWriter,并使用libxml,因此它应该非常高效。

+0

+1,因为我不知道XMLWriter会自动为你做这件事。 – Shackrock 2013-05-23 19:39:12

-1
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; 
    }  
+4

这在很多层面上都很糟糕: **(1)**无需使用正则表达式进行哑搜索和替换。 **(2)**替换值不是正确的实体(它们不以分号结尾)。 **(3)**您将分别获得每个替换版本的数组。 **(4)**这种策略甚至不是面向未来的;或者每当规范发生变化时你都会维护它? 我不知道如何得到4票。 – Christian 2015-10-29 11:00:50

-1

基于sadeghj下面的代码的解决方案为我:

/** 
* @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; 
}