Document Object Model (DOM)

Расширение DOM позволяет работать с XML-документами через DOM API.

Замечание: Расширение DOM использует кодировку UTF-8.

Создание и сохранение

Есть два способа загрузить XML документы в DOM:

  • Из файла DomDocument::load():
    $dom = new DomDocument();
    $dom->load("library.xml");
    
  • Из строки DomDocument::loadXML():
    $dom = new DomDocument();
    $dom->loadXML($xml);
    
    Так же можно импортировать HTML файлы и строки с помощью:
  • DomDocument::loadHTMLFile()
  • DomDocument::loadHTML()

Для сохранения документов:

  • DomDocument::save() - XML в файл
  • DomDocument::saveXML() - XML в строку
  • DomDocument::saveHTML() - HTML в строку
  • DomDocument:saveHTMLFile() - HTML в файл
$dom = new DomDocument();
$dom->load('library.xml');

// Do something with our XML here

// Save to file

if ($use_xhtml) {
    $dom->save('library.xml');
} else {
    $dom->saveHTMLFile('library.xml');
}

// Output the data

if ($use_xhtml) {
    echo $dom->saveXML();
} else {
    echo $dom->saveHTML();
}

Чтобы получить доступ к корневому элементу необходимо использовать свойство documentElement.

dom=new DomDocument;
$dom->Load("file.xml");
$root=$dom->documentElement; // Root node

XPath запросы

DomXPath гораздо мощнее чем эквивалент из SimpleXML. Для начала нужно создать объект класса DomXpath, которому передается объект документа DomDocument. Затем регистрируются только нужные пространства имен. В конце выполняется запрос и итерация по результатам.

$dom = new DomDocument();
$dom->load("library.xml");

$xpath = new DomXPath($dom);

$xpath->registerNamespace(
    "lib", "http://example.org/library"
);

$result = $xpath->query("//lib:title/text()");

foreach ($result as $book) {
    echo $book->data;
}

Вызов DomXpath::query() вернет объект DomNodeList, чтобы узнать сколько в нем элементов можно воспользоваться свойством length. А получить доступ к любому с помощью метода item(). Так же можно пройти по всем результам с помощью цикла foreach().

$result = $xpath->query("//lib:title/text()");

if ($result->length > 0) {
    // Random access
    $book = $result->item (0);
    echo $book->data;

    // Sequential access
    foreach ($result as $book) {
        echo $book->data;
    }
}

Изменение XML документов

Чтобы добавить новые данные в загруженный документ, следует создать объект класса DomElement с помощью следующих методов:

  • DomDocument::createElement()
  • DomDocument::createElementNS()
  • DomDocument::createTextNode()
$dom = new DomDocument();
$dom->load("library.xml");

$book = $dom->createElement("book");
$book->setAttribute("meta:isbn", "9781940111001");

$title = $dom->createElement("title");
$text = $dom->createTextNode("Mastering the SPL Library");

$title->appendChild($text);
$book->appendChild($title);

$author = $dom->createElement("author","Joshua Thijssen");
$book->appendChild($author);

$publisher = $dom->createElement(
    "pub:publisher", "musketeers.me, LLC."
);
$book->appendChild($publisher);

$dom->documentElement->appendChild($book);

Чтобы добавить элементу атрибут используется метод DomElement::setAttribute(). Чтобы добавить потомка к элементу используется DomElement::appendChild().

Перемещение данных

Так как DOM расширение не заботится о целостности документа, то следует использовать комбинацию методов:

  • DomNode::appendChild()
  • DomNode::insertBefore()
$dom = new DOMDocument();
$dom->load("library.xml");

$xpath = new DomXPath($dom);
$xpath->registerNamespace(
    "lib", "http://example.org/library"
);

$result = $xpath->query("//lib:book");
$result->item(1)->parentNode->insertBefore(
    $result->item(1), $result->item(0)
);

В этом примере берется второй book элемент и ставится на место первого. Методы DomNode::appendChild() и DomNode::insertBefore() переносят элемент из одного места в другое. Если нужно копировать элемент, то сначала следует выполнить вызов DomNode::cloneNode():

$dom = new DOMDocument();
$dom->load("library.xml");

$xpath = new DomXPath($dom);
$xpath->registerNamespace(
    "lib", "http://example.org/library"
);

$result = $xpath->query("//lib:book");

$clone = $result->item(0)->cloneNode();
$result->item(1)->parentNode->appendChild($clone);

Удаление данных

Возможны три типа данных, которые можно удалить из XML документа: атрибуты, элементы и CDATA. Для каждого типа DOM предоставляет отдельный метод:

  • DomNode::removeAttribute()
  • DomNode::removeChild()
  • DomCharacterData::deleteData()
$xml = <<<XML
<xml>
    <text type="misc">some text here</text>
    <text type="misc">some more text here</text>
    <text type="misc">yet more text here</text>
</xml>
XML;

$dom = new DOMDocument();
$dom->loadXML($xml);

$xpath = new DomXpath($dom);

$result = $xpath->query("//text");
$result->item(0)->parentNode->removeChild($result->item(0));
$result->item(1)->removeAttribute('type');

$result = $xpath->query('text()', $result->item(2));
$result->item(0)->deleteData(0, $result->item(0)->length);

echo $dom->saveXML();

В данном примере мы начинаем с того, что получаем все text элементы. Потом удаляется первый элемент из результата. Потом удаляется атрибут type.

Работа с пространствами имен

В большинстве случаев можно в функции DOM с помощью префиксов прямо передавать пространства имен:

$dom = new DomDocument();

$node = $dom->createElement('ns1:somenode');

$node->setAttribute('ns2:someattribute', 'somevalue');
$node2 = $dom->createElement('ns3:anothernode');
$node->appendChild($node2);

// Set xmlns:* attributes

$node->setAttribute('xmlns:ns1', 'http://example.org/ns1');
$node->setAttribute('xmlns:ns2', 'http://example.org/ns2');
$node->setAttribute('xmlns:ns3', 'http://example.org/ns3');

$dom->appendChild($node);

echo $dom->saveXML();

Можно упростить, используя DomDocument::createElementNS() и DomNode::setAttributeNS():

$dom = new DomDocument();

$node = $dom->createElementNS(
    'http://example.org/ns1', 'ns1:somenode'
);
$node->setAttributeNS(
« 'http://example.org/ns2',
    'ns2:someattribute',
    'somevalue'
);

$node2 = $dom->createElementNS(
    'http://example.org/ns3', 'ns3:anothernode'
);
$node3 = $dom->createElementNS(
    'http://example.org/ns1', 'ns1:someothernode'
);

$node->appendChild($node2);
$node->appendChild($node3);

$dom->appendChild($node);

$dom->formatOutput = true;
echo $dom->saveXML();

Результат выполнения будет:

<?xml version="1.0"?>
<ns1:somenode  xmlns:ns1="http://example.org/ns1"
               xmlns:ns2="http://example.org/ns2"
               xmlns:ns3="http://example.org/ns3"
               ns2:someattribute="somevalue">
    <ns3:anothernode xmlns:ns3="http://example.org/ns3"/>
    <ns1:someothernode/>
</ns1:somenode>

Взаимодействие с SimpleXML

Можно легко обмениваться документами между SimpleXML и DOM:

  • dom_import_simplexml()
  • simplexml_import_dom()