最近在使用 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) = ©<>, 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) = ©<>, Html Tag=<tag/> <?xml version="1.0"?> <root> <testcase_1>$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/></testcase_1> <testcase_2>$testString = Html Special Characters = ©<> , Html Special Characters (encode) = ©<>, Html Tag=<tag/></testcase_2> </root> Warning: SimpleXMLElement::__construct() [simplexmlelement.--construct]: Entity: line 2: parser error : Entity 'copy' not defined
發現問題了嗎?使用addChild有正確將 <> 進行 Escape,但是除了 <> 以外的 HTML 保留字卻不會轉換 (&必須轉換為&)。那要如何解決呢?最簡單的方法就是自行轉換 & 字元為 & 後再送給 addChild(),我們修改一下程式碼,如下:
$testString = '$testString = Html Special Characters = ©<> , Html Special Characters (encode) = ©<>, Html Tag=<tag/>'; echo $testString."\r\n"; $simpleXMLElement = new SimpleXMLElement('<root/>'); $simpleXMLElement->testcase_1 = $testString; $simpleXMLElement->addChild('testcase_2', preg_replace('/&/', '&', $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) = ©<>, Html Tag=<tag/> <?xml version="1.0"?> <root> <testcase_1>$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/></testcase_1> <testcase_2>$testString = Html Special Characters = ©<> , Html Special Characters (encode) = &copy;&lt;&gt;, Html Tag=<tag/></testcase_2> </root> SimpleXMLElement Object ( [testcase_1] => $testString = Html Special Characters = ©<> , Html Special Characters (encode) = ©<>, Html Tag=<tag/> [testcase_2] => $testString = Html Special Characters = ©<> , Html Special Characters (encode) = ©<>, Html Tag=<tag/> )
沒問題了,如果有使用到 addAttribute() 也要進行一樣的處理喔。當然你也可以採用 Array Access 的方式操作 DOM,但是如果遇到 Namespace 的處理就避免不了要處理這個問題了。
HTML 保留字請參考: