PHP - htmlentities()是否足以创建xml-safe值?

时间:2010-05-12 21:08:20

标签: php xml xml-serialization

我正在从头开始构建一个XML文件,并且需要知道htmlentities()是否会转换每个可能破坏XML文件的字符(以及可能的UTF-8数据)?这些值来自twitter / flickr feed,所以我需要确定!

5 个答案:

答案 0 :(得分:51)

htmlentities() 不是构建合法XML的保证方式。

如果您担心这一点,请使用htmlspecialchars()代替htmlentities()。如果您的数据表示与XML文档的编码之间存在编码不匹配,htmlentities()可能会解决/覆盖它们(这样做会使您的XML大小膨胀)。我认为最好让你的编码保持一致,只需使用htmlspecialchars()

另外,请注意,如果在由单引号分隔的XML属性中抽取htmlspecialchars()的返回值,则还需要传递ENT_QUOTES标志,以便源中的任何单引号字符串也被正确编码。无论如何,我建议这样做,因为它可以使您的代码免受因将来使用XML引用的单引号而导致的错误。

修改:澄清:

htmlentities()会将许多非ANSI字符(我假设这是UTF-8数据的含义)转换为实体(仅用ANSI字符表示)。但是,对于没有相应实体的任何字符,它不能这样做,因此不能保证其返回值仅包含ANSI字符。这就是为什么我建议不要使用它。

如果编码是一个可能的问题,请明确处理(例如使用iconv())。

编辑2 :考虑到Josh Davis的评论,改进了答案。

答案 1 :(得分:20)

Dom::createTextNode()会自动转义您的内容。

示例:

$dom = new DOMDocument;
$element = $dom->createElement('Element');
$element->appendChild(
    $dom->createTextNode('I am text with Ünicödé & HTML €ntities ©'));

$dom->appendChild($element);
echo $dom->saveXml();

输出:

<?xml version="1.0"?>
<Element>I am text with &#xDC;nic&#xF6;d&#xE9; &amp; HTML &#x20AC;ntities &#xA9;</Element>

将内部编码设置为utf-8时,例如

$dom->encoding = 'utf-8';

你仍然会得到

<?xml version="1.0" encoding="utf-8"?>
<Element>I am text with Ünicödé &amp; HTML €ntities ©</Element>

请注意,上述内容与在Dom::createElement()中设置第二个参数$value不同。该方法仅确保您的元素名称有效。请参阅手册页上的注释,例如

$dom = new DOMDocument;
$element = $dom->createElement('Element', 'I am text with Ünicödé & HTML €ntities ©');
$dom->appendChild($element);
$dom->encoding = 'utf-8';
echo $dom->saveXml();

将导致警告

Warning: DOMDocument::createElement(): unterminated entity reference  HTML €ntities ©

以及以下输出:

<?xml version="1.0" encoding="utf-8"?>
<Element>I am text with Ünicödé </Element>

答案 2 :(得分:14)

Gordon的答案很好并且解释了XML编码问题,但没有显示简单的功能(或blackbox的功能)。 Jon的答案从'htmlspecialchars'函数推荐开始,但他和其他人做了一些错误,然后我会强调。

优秀的程序员必须控制字符串和XML数据中是否使用UTF-8 :UTF-8(或其他非ASCII编码)在一致算法中是安全的。< / p>

SAFE UTF-8 XML不需要全实体编码。不加区分的编码产生“第二类,非人类可读,编码/解码需求,XML”。当所有内容都是ASCII时,安全的ASCII XML也不需要实体编码。

只需要在一串XML内容中转义3到4个字符:><&和可选"。 请阅读http://www.w3.org/TR/REC-xml/“2.4字符数据和标记”和“4.6预定义实体”。然后你可以使用'htmlentities'

为了说明,以下PHP函数将使XML完全安全:

// it is a didactic illustration, USE htmlentities($S,flag)
function xmlsafe($s,$intoQuotes=0) {
if ($intoQuotes)
    return str_replace(array('&','>','<','"'), array('&amp;','&gt;','&lt;','&quot;'), $s);
    // SAME AS htmlspecialchars($s)
else
    return str_replace(array('&','>','<'), array('&amp;','&gt;','&lt;'), $s);
    // SAME AS htmlspecialchars($s,ENT_NOQUOTES)
}

// example of SAFE XML CONSTRUCTION
function xmlTag( $element, $attribs, $contents = NULL) {
$out = '<' . $element;
foreach( $attribs as $name => $val )
   $out .= ' '.$name.'="'. xmlsafe( $val,1 ) .'"';
if ( $contents==='' || is_null($contents) )
    $out .= '/>';
else
    $out .= '>'.xmlsafe( $contents )."</$element>";
return $out;
}

在CDATA块中你不需要使用这个功能......但是,请避免不加区分地使用CDATA。

答案 3 :(得分:5)

所以你的问题是“htmlentities()的结果是否符合XML标准且符合UTF-8标准?”答案是否定的,不是。

htmlspecialchars()足以逃避XML的特殊字符,但您必须以任何方式清理UTF-8字符串。即使您使用SimpleXML构建XML,也必须清理字符串。我不知道其他库,如XMLWriter或DOM,我认为它是一样的。

答案 4 :(得分:1)

以为我会为那些需要消毒的人添加这个。不要丢失XML属性。

// Returns SimpleXML Safe XML keeping the elements attributes as well
function sanitizeXML($xml_content, $xml_followdepth=true){

    if (preg_match_all('%<((\w+)\s?.*?)>(.+?)</\2>%si', $xml_content, $xmlElements, PREG_SET_ORDER)) {

        $xmlSafeContent = '';

        foreach($xmlElements as $xmlElem){
            $xmlSafeContent .= '<'.$xmlElem['1'].'>';
            if (preg_match('%<((\w+)\s?.*?)>(.+?)</\2>%si', $xmlElem['3'])) {
                $xmlSafeContent .= sanitizeXML($xmlElem['3'], false);
            }else{
                $xmlSafeContent .= htmlspecialchars($xmlElem['3'],ENT_NOQUOTES);
            }
            $xmlSafeContent .= '</'.$xmlElem['2'].'>';
        }

        if(!$xml_followdepth)
            return $xmlSafeContent;
        else
            return "<?xml version='1.0' encoding='UTF-8'?>".$xmlSafeContent;

    } else {
        return htmlspecialchars($xml_content,ENT_NOQUOTES);
    }

}

<强>用法:

$body = <<<EG
<?xml version='1.0' encoding='UTF-8'?>
<searchResult count="1">
   <item>
      <title>2016 & Au Rendez-Vous Des Enfoir&</title>
   </item>
</searchResult>
EG;
$newXml = sanitizeXML($body);
var_dump($newXml);

<强>返回:

<?xml version='1.0' encoding='UTF-8'?>
<searchResult count="1">
    <item>
        <title>2016 &amp; Au Rendez-Vous Des Enfoir&amp;</title>
    </item>
</searchResult>