SimpleXML
Расширение SimpleXML предоставляет очень простой и легкий в использовании набор инструментов для преобразования XML в объект, с которым можно затем работать через его свойства и с помощью итераторов.
Парсинг
Исходный документ:
<?xml version="1.0"?>
<library>
<book isbn="0345342968">
<title>Fahrenheit 451</title>
<author>R. Bradbury</author>
<publisher>Del Rey</publisher>
</book>
<book isbn="0048231398">
<title>The Silmarillion</title>
<author>J.R.R. Tolkien</author>
<publisher>G. Allen & Unwin</publisher>
</book>
<book isbn="0451524934">
<title>1984</title>
<author>G. Orwell</author>
<publisher>Signet</publisher>
</book>
<book isbn="031219126X">
<title>Frankenstein</title>
<author>M. Shelley</author>
<publisher>Bedford</publisher>
</book>
<book isbn="0312863551">
<title>The Moon Is a Harsh Mistress</title>
<author>R. A. Heinlein</author>
<publisher>Orb</publisher>
</book>
</library>
SimpleXML поддерживает документы только версии 1.0. При попытке парсинга 1.1 будет выдано предупреждение и парсинг не удастся. Так как SimpleXML при парсинге загружает весь документ в память, то следует избегать парсинга слишком больших документов.
Все объекты, созданные с помощью SimpleXML являются объектами класса SimpleXMLElement. Чтобы его создать можно воспользоваться процедурным или объектно-ориентированным способом.
Процедурный подход:
- simplexml_load_string() загружает XML из строки.
- simplexml_load_file() загружает XML из файла.
// Load an XML string
$xmlstr = file_get_contents('library.xml');
$library = simplexml_load_string($xmlstr);
// Load an XML file
$library = simplexml_load_file('library.xml');
ООП подход:
// Load an XML string
$xmlstr = file_get_contents('library.xml');
$library = new SimpleXMLElement($xmlstr);
// Load an XML file
$library = new SimpleXMLElement('library.xml', NULL, true);
В конструкторе класса SimpleXMLElement второй параметр - это дополнительные опции для библиотеки libxml, а третий - флаг, указывающий что первый параметр представляет собой путь к файлу или же строку с XML.
Доступ к потомкам и атрибутам
Когда SimpleXML парсит документ, то она конвертирует все листы и атрибуты в свойства результирующего SimpleXMLElement объекта. Атрибуты ковертируются в ассоциативный массив, который может быть доступен из свойства, которому они принадлежат. В свою очередь каждое свойство так же является объектом SimpleXMLElement:
$library = new SimpleXMLElement('library.xml', NULL, true);
foreach ($library->book as $book) {
echo $book['isbn'] . "\n";
echo $book->title . "\n";
echo $book->author . "\n";
echo $book->publisher . "\n\n";
}
В данном подходе обязательным условием является то, что надо знать имена всех элеметов и аттрибутов документа, иначе невозможно будет добраться до них. В случаях же когда мы заранее не знаем имена элементов документа, можно воспользоваться другим функционалом:
- SimpleXMLElement::children()
- SimpleXMLElement::attributes()
- SimpleXMLElement::getName()
- SimpleXMLElement::count()
foreach ($library->children() as $child) {
echo $child->getName() . ":\n";
// Get attributes of this element
foreach ($child->attributes() as $attr) {
echo ' ' . $attr->getName() . ': ' . $attr . "\n";
}
// Get children
foreach ($child->children() as $subchild) {
echo ' ' . $subchild->getName() . ': ' . $subchild . "\n";
}
echo "\n";
}
Таким образом можно добраться до любого элемента или свойства XML документа, даже не зная его имени.
XPath запросы
The XML Path Language (XPath) - язык запросов для поиска и доступа в XML документах. Используется метод SimpleXMLElement::xpath(). С помощью его можно выполнить XPath запрос над любым SimpleXMLElement объектом. Если применяется к корневому документу, то поиск будет произведен по всему документу. Если применем к потомку, то будет произведен только в потомке или во всех его потомках. XPath вернет массив элементов, даже если найден был всего один.
// Search the root element
$results = $library->xpath('/library/book/title');
foreach ($results as $title) {
echo $title . "\n";
}
// Search the first child element
$results = $library->book[0]->xpath('title');
foreach ($results as $title) {
echo $title . "\n";
}
Изменение документа
- SimpleXMLElement::addChild()
- SimpleXMLElement::addAttribute()
addChild() принимает три параметра, первый - имя нового элемента. Второй - опциональное значение элемента, третий - опциональное пространство имен, к которому элемент пренадлежит. Так как addChild() возвращает объект SimpleXMLElement то можно сохранить результат в переменной и потом к ней еще добавлять узлы и атрибуты.
$book = $library->addChild('book');
$book->addAttribute('isbn', '0812550706');
$book->addChild('title', "Ender's Game");
$book->addChild('author', 'Orson Scott Card');
$book->addChild('publisher', 'Tor Science Fiction');
header('Content-type: text/xml');
echo $library->asXML();
Этот код добавляет элемент book к объекту library. И затем к нему добавляются еще 3 потомка и атрибут. Чтобы сохранить результат вызывается метод asXML(). Вызванный без параметров, он вернет XML строку. Так же можно передать путь к файлу, в который будет сохранен XML.
Замечание: если файл уже существует, то вызов asXML() перезатрет его содержимое.
SimpleXML не предоставляет средств для удаления узлов или атрибутов. Для этого следует экспортировать SimpleXMLElement в DOM.
Работа с пространствами имен
Использование пространства имен в XML позволяет ассоциировать определенные элементы и имена атрибутов с пространствами имен, заданными через URI. Это позволяет избежать потенциальных конфликтов имен, когда существуют два элемента, которые содержат разные данные.
Пример документа с пространствами имен:
<?xml version="1.0"?>
<library xmlns="http://example.org/library"
xmlns:meta="http://example.org/book-meta"
xmlns:pub="http://example.org/publisher"
xmlns:foo="http://example.org/foo">
<book meta:isbn="0345342968">
<title>Fahrenheit 451</title>
<author>Ray Bradbury</author>
<pub:publisher>Del Rey</pub:publisher>
</book>
</library>
SimpleXMLElement::getDocNamespaces() - вернет массив всех пространств имен, объявленных в документе. По умолчанию метод вернет только пространства имен, объявленные в корневом элементе. Если же передать параметром TRUE, то рекурсивно будет обойден весь документ:
$namespaces = $library->getDocNamespaces();
foreach ($namespaces as $key => $value) {
echo "{$key} => {$value}\n";
}
/* outputs:
=> http://example.org/library
meta => http://example.org/book-meta
pub => http://example.org/publisher
foo => http://example.org/foo
*/
Несмотря на то, что пространство имен foo нигде не используется, оно было в результирующем массиве. Чтобы получить результат только из используемых в документе имен, нужно восмользоваться SimpleXMLElement::getNamespaces():
$namespaces = $library->getNamespaces(true);
foreach ($namespaces as $key => $value) {
echo "{$key} => {$value}\n";
}
/* outputs:
=> http://example.org/library
meta => http://example.org/book-meta
pub => http://example.org/publisher
*/