今からお前んちこいよ

ベルリンにて細々とお勉強。

PHPでのXMLパースエラー出力は必須だね

日本語 | English

PHPでXMLパースを

$xml = simplexml_load_string($data);  

で終わらせていて痛い目にあった。
失敗したらfalseが返ってくるし大丈夫だと思っていたが、

  • 制御文字コードが含まれていると失敗してしまう
  • 他パースエラーが発生した場合の原因がわからない

上記2つが懸念点。
 

制御文字コードが含まれていると失敗してしまう

※ 制御文字コードとは? → 制御コード表

私の場合、以下の2つの方法で制御文字コードの存在を確認できた。
1. chromeでファイルを開いた際に、エラーが表示された。 f:id:hakopako03:20140510171613p:plain

2. sublime text にて制御文字コードが記号として表示された。 f:id:hakopako03:20140510171622p:plain

ちなみに、制御文字コードの出力はバイナリエディタを使用しました。
私が使っているのは、0xED というエディタ。
以下の様に16進数で入力し、保存すれば制御文字を出力できます。
f:id:hakopako03:20140510171632p:plain

これが含まれているとパースエラーとなるので、
パース前に置換する必要がある

$search = array("\0", "\x01", "\x02", "\x03", "\x04", "\x05","\x06", "\x07", "\x08", "\x0b", "\x0c", "\x0e", "\x0f");  
$data = str_replace($search, '', $data);  
$xml = simplexml_load_string($data);  

 

他パースエラーが発生した場合の原因がわからない

上記の制御文字コードをエラーとして試しに出力しようとしたら、
try catchで例外としてキャッチできなかった。
どうやら warning エラー。
参考:How do I handle Warning: SimpleXMLElement::__construct()?

set_error_handler を使う。

set_error_handler(function($errno, $errstr, $errfile, $errline) {  
    throw new Exception($errstr, $errno);  
});  

try {  
    $search = array("\0", "\x01", "\x02", "\x03", "\x04", "\x05","\x06", "\x07", "\x08", "\x0b", "\x0c", "\x0e", "\x0f");  
    $data = str_replace($search, '', $data);  
    $xml = simplexml_load_string($data);  
    restore_error_handler(); //追記:2014.05.05  
}catch(Exception $e) {  
    restore_error_handler();  
    echo $e;  
}  

これで回避出来なかったパースエラーを出力できる

---- 追記 (2014/05/05) ----
set_error_handler()を使用することで
一時的にwarningをfatalへとエラーレベルを上げることとなる。

これだと他の箇所でwarningがあると引きずられてfatalになってしまうので、
catch時以外もパース後にrestore_error_handler()をしてやり
エラーレベルを戻すようにしました。
(php5.5以上ならfinallyとかでもいいかもしれない)