はじめに
PHPのSimpleXMLの動きがわりと不可思議なのでまとめです。
テキストノードのエスケープ
SimpleXMLでテキスト入れるには大体3つくらいのやり方があります。
$xml = new SimpleXMLElement('<root></root>');
$text = '"PHP&XML!"\(>_@)/';
/*1つめ*/
$item1 = $xml->addChild('item1');
$item1[0] = $text;
$this->assertEquals($text,$item1[0]);
/*2つめ*/
$text2 = $xml->addChild('item2')->{0} = $text;
$this->assertEquals($text,$xml->item2[0]);
/*3つめ*/
$item3 = $xml->addChild('item3',$text);
$this->assertEquals($text,$item3[0]);
このうち3つ目はアサートが通りません。
addChild
の第二引数でテキストを入れた場合、
アンパサンドがなぜかエスケープしてくれません。不思議です。
2つ目は一行でエレメントの作成とテキストの入力ができて便利ですが、
返り値が当然SimpleXMLElementでなくテキストになるので注意です。
$item[0]
で入力して、$item[0]
で出力するのが安定です。
属性のエスケープ
属性の入力は二通りあります。
$xml = new SimpleXMLElement('<root></root>');
$text = '"PHP&XML!"\(>_@)/';
/*1つめ*/
$item1 = $xml->addChild('item1');
$item1->addAttribute('attr1',$text);
$this->assertEquals($text,$item1['attr1']);
/*2つめ*/
$item2 = $xml->addChild('item2');
$item2['attr2'] = $text;
$this->assertEquals($text,$item2['attr2']);
addChild
の挙動を考えるとaddAttribute
もエスケープに失敗しそうですが、
こちらは両方ともキチンとエスケープしてくれ、SimpleXMLに触れる人間を混乱に陥れます。
XPathのエスケープ
存在しません。
関数が用意されてないので正規表現頑張ろう死のうってレベルではないです。
SimpleXMLのXPathは1.0なのでエスケープのまともな定義自体が存在していません。
次のようなコードを書くといずれ死にます。
$target = current($xml->xpath('./haystack/book[title/text()="' . $needle. '"]'));
foreachで地道に探索しましょう。
$target = null;
foreach($xml->xpath('./haystack/book') as $book){
if ($book->title[0] === $needle){
$target = $book;
break;
}
}
終わりに
ちなみにエレメントや属性は次のようにunset
で削除できます。
$target = $xml->item1;
/*属性の削除*/
unset($target['attr1']);
/*エレメントの削除*/
unset($target[0]);
$input[0] = $text
のことなどを合わせて考えると、
SimpleXMLにはXMLを配列的に扱いたい的な雰囲気があります。