以下のXMLを使用してサンプルコードを解説する。
<?xml version="1.0"?>
<library>
<book isbn="0111222333">
<title>She sells seashells</title>
<author>Emily</author>
<publisher>store</publisher>
</book>
<book isbn="0123456789">
<title>Eight apes ate eight apples</title>
<author>John</author>
<publisher>shop</publisher>
</book>
<book isbn="9876543210">
<title>Strike</title>
<author>Mike</author>
<publisher>shop</publisher>
</book>
<book isbn="1234567890">
<title>Shine</title>
<author>Jack</author>
<publisher>store</publisher>
</book>
</library>
■XMLドキュメントのパース
- 対応しているXMLのバージョンは1.0(1.1は非対応)
- パースに失敗すると警告を発する
- ドキュメントをパースするときは、SimpleXMLElementオブジェクトを生成する必要がある
<?php
$xml = simplexml_load_file('doc/library.xml');
?>
上述のコードは以下のコードと同じ働きをする。
<?php
$doc = file_get_contents('doc/library.xml');
$xml = simplexml_load_string($doc);
?>
オブジェクト指向型のアプローチ
以下のようにSimpleXMLElementクラスを使って、アブじぇ句と指向型のアプローチを取ることもできる。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
?>
上述のコードは以下のコードと同じ働きをする。
<?php
$doc = file_get_contents('doc/library.xml');
$xml = new SimpleXMLElement($doc);
?>
個人的にはオブジェクト指向型のアプローチの方がコードがスッキリして好きだ。
SimpleXMLElement(string $dat[, int $options[, bool $flag_uri[, string $namespace[, bool $is_prefix]]]])
- $dat
- XML文字列。URLで指定する場合は、第三引数を「true」にしなければならない
- $options
- libxmlに渡すパラメータ。用途にもよるが、「null」を渡して使用することが多い
- $flag_uri
- 第一引数がURLの場合は、「true」をセットしなくてはならない
■要素と属性へのアクセス
各要素にアクセスするには「->」を使用し、属性にアクセスするためには配列リテラルを使用する。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
foreach($xml->book as $book){
print($book['isbn'] . PHP_EOL);
print($book->title . PHP_EOL);
print($book->author . PHP_EOL);
print($book->publisher . PHP_EOL);
}
/*
0111222333
She sells seashells
Emily
store
-----
0123456789
Eight apes ate eight apples
John
shop
-----
9876543210
Strike
Mike
shop
-----
1234567890
Shine
Jack
store
-----
*/
?>
但し、上述のコードには「子要素や属性の名前を知っていなければならない」という欠点がある。従って、以下のコードのように、要素名や属性名を取得するメソッドを使う方が良い。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
foreach($xml->children() as $child){
print($child->getName() . PHP_EOL);
foreach($child->attributes() as $attr){
print("\t" . $attr->getName() . ': ' . $attr . PHP_EOL);
}
foreach($child->children() as $sub){
print("\t" . $sub->getName() . ': ' . $sub . PHP_EOL);
}
print('-----' . PHP_EOL);
}
/*
book
isbn: 0111222333
title: She sells seashells
author: Emily
publisher: store
-----
book
isbn: 0123456789
title: Eight apes ate eight apples
author: John
publisher: shop
-----
book
isbn: 9876543210
title: Strike
author: Mike
publisher: shop
-----
book
isbn: 1234567890
title: Shine
author: Jack
publisher: store
-----
*/
?>
但し、上述のコードでは深さが特定の場合しか探索できない。深さが不定の場合は、再帰関数やRecursiveIteratorIteratorクラスなどを使用する。
一度値を配列に格納する方法
すぐに出力する場合は上述の方法で問題はないが、配列に要素の文字列を格納する場合はSimpleXMLElementオブジェクトから文字列に変換する。
$data = new SimpleXMLElement($url, null, true);
if($data->getName() === 'rss'){//rss
foreach($data->channel->item as $i){
$rv[] = array(
'title' => ((string)$i->title),
'link' => ((string)$i->link)
);
}
}
elseif($data->getName() === 'feed'){//atom
foreach($data->entry as $item){
$rv[] = array(
'title' => ((string)$item->title),
'link' => ((string)$item->link['href'])
);
}
}
各メソッドについて
- SimpleXMLElement::children()
- 指定したノードの子ノードを見つける
- SimpleXMLElement::attributes()
- 属性名をキーとして、指定したノードの属性値の配列を返す
- SimpleXMLElement::getName()
- 要素名、属性名を返す
■XPathクエリ
XPathを使用して特定の要素を抜き出したりするには、以下のように「SimpleXMLElement::xpath」を用いる。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
$elm = $xml->xpath('/library/book/title');
foreach($eml as $title){
print($title . PHP_EOL);
}
/*
She sells seashells
Eight apes ate eight apples
Strike
Shine
*/
/*
array(4) {
[0]=>
object(SimpleXMLElement)#2 (1) {
[0]=>
string(19) "She sells seashells"
}
[1]=>
object(SimpleXMLElement)#3 (1) {
[0]=>
string(27) "Eight apes ate eight apples"
}
[2]=>
object(SimpleXMLElement)#4 (1) {
[0]=>
string(6) "Strike"
}
[3]=>
object(SimpleXMLElement)#5 (1) {
[0]=>
string(5) "Shine"
}
}
*/
?>
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
$elm = $xml->book[0]->xpath('title');
foreach($elm as $title){
print($title . PHP_EOL);
}
//She sells seashells
?>
SimpleXMLElement::xpathメソッドのについて
xpathクエリを実行し、結果をSimpleXMLElementオブジェクトとして返す。
■SimpleXMLによるXMLドキュメントの操作
以下のように、PHP5.1.3以降ではSimpleXMLで要素の追加が行えるようになった。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
$book = $xml->addChild('book');
$book->addAttribute('isbn', '0812550706');
$book->addChild('title', 'She sells seashells');
$book->addChild('author', 'John');
$book->addChild('publisher', 'amazon');
header('Content-type: text/xml');
print($xml->asXML());
/*
<?xml version="1.0" encoding="utf-8"?>
<library>
<book isbn="0111222333">
<title>She sells seashells</title>
<author>Emily</author>
<publisher>store</publisher>
</book>
<book isbn="0123456789">
<title>Eight apes ate eight apples</title>
<author>John</author>
<publisher>shop</publisher>
</book>
<book isbn="9876543210">
<title>Strike</title>
<author>Mike</author>
<publisher>shop</publisher>
</book>
<book isbn="1234567890">
<title>Shine</title>
<author>Jack</author>
<publisher>store</publisher>
</book>
<book isbn="0812550706"><title>She sells seashells</title><author>John</author><publisher>amazon</publisher></book></library>
*/
?>
asXMLメソッドはXML文字列を返す。また、引数にファイルパスを指定した場合は、XMLドキュメントとして保存される。但し、既にファイルが存在している場合は、警告なしにファイルが上書きされるので注意が必要である。
要素の削除
SimpleXMLは要素や属性の追加についてのメソッドを持っているが、削除についてのメソッドは存在しない。削除する場合は以下のようにする。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
$xml->book[0] = null;
/*
<?xml version="1.0" encoding="utf-8"?>
<library>
<book isbn="0111222333"></book>
<book isbn="0123456789">
<title>Eight apes ate eight apples</title>
<author>John</author>
<publisher>shop</publisher>
</book>
<book isbn="9876543210">
<title>Strike</title>
<author>Mike</author>
<publisher>shop</publisher>
</book>
<book isbn="1234567890">
<title>Shine</title>
<author>Jack</author>
<publisher>store</publisher>
</book>
</library>
*/
?>
但し、この方法は要素内を空にするだけであり、book要素の属性も属性値を空にすることしかできない。もしも、完全に削除したい場合は、DOMへエクスポートする必要がある。以下のようにする。
<?php
$xml = new SimpleXMLElement('doc/library.xml', null, true);
unset($xml->book[0]);
/*
<?xml version="1.0" encoding="utf-8"?>
<library>
<book isbn="0123456789">
<title>Eight apes ate eight apples</title>
<author>John</author>
<publisher>shop</publisher>
</book>
<book isbn="9876543210">
<title>Strike</title>
<author>Mike</author>
<publisher>shop</publisher>
</book>
<book isbn="1234567890">
<title>Shine</title>
<author>Jack</author>
<publisher>store</publisher>
</book>
</library>
*/
?>
各メソッドについて
- SimpleXMLElement::addChild
- 要素を追加する。戻り値はSimpleXMLElementオブジェクトであり、これに対して操作することでルートのXMLを操作することもできる
- SimpleXMLElement::addAttribute
- 属性を追加する
■名前空間
<?xml version="1.0"?>
<library xmlns="http://sample.org/library" xmlns:meta="http://sample.org/book-meta" xmlns:pub="http://sample.org/publisher" xmlns:sample="http://sample.org/sample">
<book meta:isbn="0345342968">
<title>Fahrenheit 451</title>
<author>Ray Bradbury</author>
<pub:publisher>Del Rey</pub:publisher>
</book>
</library>
<?php
$xml = new SimpleXMLElement('test.xml', null, true);
$ns = $xml->getDocNamespaces();
foreach($ns as $key => $value){
print("{$key}: {$value}\n");
}
/*
: http://sample.org/library
meta: http://sample.org/book-meta
pub: http://sample.org/publisher
sample: http://sample.org/sample
*/
?>
<?php
$xml = new SimpleXMLElement('test.xml', null, true);
$ns = $xml->getNamespaces(true);
foreach($ns as $key => $value){
print("{$key}: {$value}\n");
}
/*
: http://sample.org/library
meta: http://sample.org/book-meta
pub: http://sample.org/publisher
*/
?>
以下のように、引数にfalseを指定するとカレントノードの名前空間を返す。名前空間が指定されていない場合は、空の配列となる。
<?php
$xml = new SimpleXMLElement('test.xml', null, true);
$ns = $xml->getNamespaces(false);
foreach($ns as $key => $value){
print("{$key}: {$value}\n");
}
/*
: http://sample.org/library
*/
?>
各メソッドについて
- SimpleXMLElement::getDocNamespaces
- ドキュメントで宣言されている名前空間を返す。引数にはbool値が入り(デフォルトはfalse)trueを指定すると、子ノードで宣言されている名前空間も返す。
- SimpleXMLElement::getNamespaces
- ドキュメントで使用している名前空間を返す。引数にはbool値が入り(デフォルトはfalse)trueを指定すると、子ノードで使用している名前空間も返す。