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 &amp; 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
*/