fbpx

PHP SimpleXML Escaping (處理HTML Special Characters) 發生問題

最近在使用 PHP 5.2 的 SimpleXML 的時候發現了一個奇怪的問題,就是 SimpleXML 對於處理 HTML Special Characters 有不一致處理行為的情況。

這樣的問題將造成由 SimpleXML 產生的 XML 無法再次由 SimpleXML 開啟,我認為這是一個嚴重的 Bug。以下是官方 Bug Trace System 相關的討論文獻。

http://bugs.php.net/bug.php?id=36795

主要的問題是在於使用 SimpleXMLElement::addChild() 與 SimpleXMLElement::addAttribute() 在設定文字時,僅會針對 <> 進行 Escaping 的動作,對於其他的 HTML Special Characters 並不會進行處理。問題來了,當使用 Array Access 的方式操作 DOM 時,卻不會發生這樣的情況。

讓我們寫支程式來測試一下

$testString = '$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/>';
echo $testString."\r\n";
$simpleXMLElement = new SimpleXMLElement('<root/>');
$simpleXMLElement->testcase_1 = $testString;
$simpleXMLElement->addChild('testcase_2', $testString);
$xml = $simpleXMLElement->asXml();
echo $xml."\r\n";
try {
    $readSimpleXMLElement = new SimpleXMLElement($xml);
} catch (Exception $e) {
    echo 'no~~';
}
print_r($readSimpleXMLElement);

程式執行結果

$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;<>, Html Tag=<tag/>
<?xml version="1.0"?>
<root>
<testcase_1>$testString = Html Special Characters = &#xA9;<> , Html Special Characters (encode) = &amp;copy;&amp;lt;&amp;gt;, Html Tag=<tag/></testcase_1>
<testcase_2>$testString = Html Special Characters = &#xA9;<> , Html Special Characters (encode) = &copy;<>, Html Tag=<tag/></testcase_2>
</root>
Warning: SimpleXMLElement::__construct() [simplexmlelement.--construct]: Entity: line 2: parser error : Entity 'copy' not defined

發現問題了嗎?使用addChild有正確將 <> 進行 Escape,但是除了 <> 以外的 HTML 保留字卻不會轉換 (&必須轉換為&amp;)。那要如何解決呢?最簡單的方法就是自行轉換 & 字元為 &amp; 後再送給 addChild(),我們修改一下程式碼,如下:

$testString = '$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/>';
echo $testString."\r\n";
$simpleXMLElement = new SimpleXMLElement('<root/>');
$simpleXMLElement->testcase_1 = $testString;
$simpleXMLElement->addChild('testcase_2', preg_replace('/&/', '&amp;', $testString));
$xml = $simpleXMLElement->asXml();
echo $xml."\r\n";
try {
    $readSimpleXMLElement = new SimpleXMLElement($xml);
} catch (Exception $e) {
    echo 'no~~';
}
print_r($readSimpleXMLElement);

程式執行結果

$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/>
<?xml version="1.0"?>
<root>
<testcase_1>$testString = Html Special Characters = &#xA9;&lt;&gt; , Html Special Characters (encode) = &amp;copy;&amp;lt;&amp;gt;, Html Tag=&lt;tag/&gt;</testcase_1>
<testcase_2>$testString = Html Special Characters = &#xA9;&lt;&gt; , Html Special Characters (encode) = &amp;copy;&amp;lt;&amp;gt;, Html Tag=&lt;tag/&gt;</testcase_2>
</root>
SimpleXMLElement Object
(
    [testcase_1] => $testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/>
    [testcase_2] => $testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/>
)

沒問題了,如果有使用到 addAttribute() 也要進行一樣的處理喔。當然你也可以採用 Array Access 的方式操作 DOM,但是如果遇到 Namespace 的處理就避免不了要處理這個問題了。

HTML 保留字請參考:

http://www.degraeve.com/reference/specialcharacters.php

發佈留言