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

Posted

技术标签:

【中文标题】在 PHP 中生成 XML 文档(转义字符)【英文标题】:Generating XML document in PHP (escape characters) 【发布时间】:2011-04-26 19:42:24 【问题描述】:

我正在从 php 脚本生成 XML 文档,我需要转义 XML 特殊字符。 我知道应该转义的字符列表;但是正确的方法是什么?

字符应该只用反斜杠(\')转义还是正确的方法是什么? 是否有任何内置的 PHP 函数可以为我处理这个问题?

【问题讨论】:

@Tchalvak:你在赏金描述中批评的许多观点都是错误的。我试图通过现有答案使其可见,希望这会有所帮助。 我没有在回答中建议将 DOM API 仅用于字符串转义。我建议您使用该 API 生成整个 XML 文档。这是对您在赏金描述中提到的问题的回应。 【参考方案1】:

我创建了一个简单的函数,它使用 XML 中的 five "predefined entities" 进行转义:

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

【讨论】:

您需要将"&amp;""&amp;amp;" 数组元素移到开头,否则创建的所有其他实体也将被替换。此外,strtr 解决方案似乎根本不起作用。 对于 OP 回答他自己的问题,这里确实存在严重的缺陷。我试图编辑它,但我想知道是否使用了原始代码;) - @Nicky De Maeyer:是的,XML 中有五个预定义的实体,我放置了一个链接。 是的,但是属性转义不同于元素转义(即您放入属性中的内容)。 See my Java code for an example。你不想做@TomasJancik 正在做的事情。 请不要!。使用图书馆或做正确的事。【参考方案2】:

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


编辑:这被@Tchalvak批评了:

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

这是错误的,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);

见Demo

【讨论】:

根据bugs.php.net/bug.php?id=31191,您特别想使用createTextNode 函数以获得正确的自动转义。 我认为@Tchalvak 的问题在于它不是基于流的。那就是使用 DOM 会创建一堆对象。正如我在answer 中提到的那样,他可以使用 XMLWriter 或者只是将我的 Java 代码移植到 PHP 以进行正确的转义(Tomas Jancik 的方式不正确)。 哎呀,eval.in/10980 是使用本机处理的xmlentities() 函数的正确实现。可能有一些开销,但是,使用本机/面向未来的功能可能值得高枕无忧。 呃,它使用 dom 对象似乎无法处理双引号或单引号:eval.in/11043 @Pete:我最终使用了您的单线解决方案。它工作得很好,除了它对诸如 å=>å 之类的东西进行编码。和 ø=>ø即使它们可以安全地保持未编码状态。为了解决这个问题,我必须指定字符集,并使用 saveXml 而不是 savehtmlnew DOMDocument('1.0', 'utf-8'))-&gt;appendChild(new DOMText($value))-&gt;parentNode-&gt;saveXml()。 PS:我认为您的评论可能应该是它自己的答案。【参考方案3】:

htmlspecialchars() 函数呢?

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

注意:ENT_XML1 标志仅在您拥有 PHP 5.4.0 或更高版本时可用。

htmlspecialchars() 用这些参数替换以下字符:

&amp;amp;(和号)变为&amp;amp; "(双引号)变为&amp;quot; '(单引号)变为&amp;apos; &amp;lt;(小于)变为&amp;lt; &amp;gt;(大于)变为&amp;gt;

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

【讨论】:

对于 XML 兼容编码,没有特别需要使用 ENT_XML1 - 至少对于 PHP 版本 4.3.0 到 5.5.0alpha4。如果您可以使用数字而不是命名实体,那么简单的 htmlspecialchars($input, ENT_QUOTES, $encoding); 也可以完成这项工作。【参考方案4】:

努力处理XML实体问题,这样解决:

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

【讨论】:

只有当这些实体在 XML 中定义时才有效,请参阅w3.org/TR/xml-entity-names【参考方案5】:

为了获得一个有效的最终 XML 文本,您需要转义所有 XML 实体,并以与 XML 文档处理指令所述相同的编码编写文本(&lt;?xml 行中的“编码”) .重音字符不需要转义,只要编码为文档即可。

但是,在许多情况下,简单地用htmlspecialchars 转义输入可能会导致实体被双重编码(例如&amp;amp;eacute; 会变成&amp;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() 都无法处理。我只能通过补充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;

【讨论】:

【参考方案6】:

如果你需要正确的 xml 输出,simplexml 是最好的选择:

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

【讨论】:

【参考方案7】:

正确的转义是获得正确 XML 输出的方法,但您需要为 属性元素 处理 不同 的转义。 (那是托马斯的回答不正确)。

不久前我写/偷了一些Java code 来区分属性和元素转义。原因是 XML 解析器认为所有空白都是特殊的,尤其是在属性中。

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

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

【讨论】:

+1 因为我不知道 XMLWriter 会自动为您完成这项工作。【参考方案8】:

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

这样所有实体 (html/xml) 都会被转义,您可以将字符串放在 XML 标记中

【讨论】:

这是一个糟糕的解决方案,因为 HTML 实体是比 XML 实体更大的集合,并且大多数 XML 解析器无法识别许多不在 XML 实体列表中的 HTML 实体。【参考方案9】:

基于 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;

【讨论】:

【参考方案10】:
 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;
         

【讨论】:

这在很多方面都很糟糕:(1) 无需使用正则表达式进行愚蠢的搜索和替换。 (2) 替换值不是正确的实体(它们不以分号结尾)。 (3) 您将分别获得每个替换版本的数组。 (4) 这种策略甚至不是面向未来的;或者您每次规格更改时都要维护它?我不知道这是怎么得到 4 票的。

以上是关于在 PHP 中生成 XML 文档(转义字符)的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中转义字符'\t'的含义?

js转义特别符号

[转]xml文件中的转义字符

微信小程序中转义字符的处理

aspose.word 把Word转成图片时,格式变了

mybatis sql语句中转义字符