PHP XML DOM
Узнайте, как использовать расширение PHP DOM для загрузки, создания, изменения, запросов (XPath) и сохранения XML-документов.
Для чего предназначено расширение DOM
Расширение DOM (Document Object Model) представляет XML-документ в виде дерева узлов в памяти. Каждый элемент, атрибут, текстовый фрагмент и комментарий является объектом-узлом, который можно читать, перемещать, добавлять или удалять. Поскольку весь документ загружается в оперативную память, DOM является правильным инструментом, когда нужно изменять XML, создавать документ с нуля или выполнять XPath-запросы, перемещающиеся по дереву.
На этой странице рассматриваются загрузка, создание, изменение, запросы и сохранение XML с помощью DOMDocument. Сравнение с более лёгкими альтернативами:
- Используйте расширение SimpleXML, когда нужно лишь читать правильно сформированный XML быстро и с минимумом кода.
- Используйте XML Parser (Expat) для потоковой обработки очень больших файлов, которые не должны полностью загружаться в память.
- Обращайтесь к DOM (эта страница), когда нужен полный контроль чтения/записи и XPath.
DOM и libxml используют общую систему ошибок; см. PHP libxml для управления ошибками разбора.
Включение расширения
Расширение DOM поставляется с PHP и включено по умолчанию в большинстве сборок (входит в пакет php-xml на Debian/Ubuntu). Вы можете убедиться в его доступности:
<?php
var_dump(extension_loaded('dom')); // bool(true)Если расширение отсутствует, установите пакет php-xml для вашей платформы или включите extension=dom в php.ini, затем перезапустите веб-сервер.
Загрузка XML-документа
Создайте DOMDocument и загрузите XML из строки с помощью loadXML() или из файла с помощью load(). Заранее включите libxml_use_internal_errors(true), чтобы некорректная разметка не выводила предупреждения — вместо этого вы сами собираете ошибки.
<?php
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1"><title>PHP for Beginners</title></book>
<book id="2"><title>Advanced XML</title></book>
</books>
XML;
libxml_use_internal_errors(true);
$doc = new DOMDocument();
if (!$doc->loadXML($xml)) {
foreach (libxml_get_errors() as $error) {
echo trim($error->message) . "\n";
}
libxml_clear_errors();
exit;
}
$root = $doc->documentElement;
echo "Root element: {$root->nodeName}\n";
foreach ($root->getElementsByTagName('title') as $title) {
echo "Title: {$title->nodeValue}\n";
}Вывод:
Root element: books
Title: PHP for Beginners
Title: Advanced XMLgetElementsByTagName() возвращает живой DOMNodeList, по которому можно сразу проходить через foreach — это обычно понятнее, чем обход childNodes, который также включает текстовые узлы с пробелами между элементами.
Создание XML-документа с нуля
Создавайте документ, добавляя узлы к DOMDocument и прикрепляя их к родительскому элементу. Установите formatOutput = true, чтобы получить отформатированный, читаемый вывод из saveXML().
<?php
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true;
$root = $doc->createElement('books');
$doc->appendChild($root);
$book = $doc->createElement('book');
$book->setAttribute('id', '1');
$root->appendChild($book);
$title = $doc->createElement('title', 'PHP for Beginners');
$book->appendChild($title);
echo $doc->saveXML();Вывод:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1">
<title>PHP for Beginners</title>
</book>
</books>Ключевые методы:
createElement($name, $value)— создаёт элемент, опционально с текстовым содержимым.setAttribute($name, $value)— добавляет или перезаписывает атрибут элемента.appendChild($node)— прикрепляет узел как последнего дочернего элемента к родителю.
Важно: передача текста напрямую в
createElement('title', $userInput)не экранирует&,<или>надёжно. Для недоверенного текста добавляйте текстовый узел:$el->appendChild($doc->createTextNode($userInput)), который всегда экранирует специальные символы.
Изменение и удаление узлов
Поскольку всё дерево находится в памяти, вы можете изменять текст, обновлять атрибуты и удалять элементы перед сохранением. Удобный способ найти нужный узел — XPath (рассматривается далее), но здесь мы используем его непосредственно:
<?php
$xml = <<<XML
<books>
<book id="1"><title>PHP for Beginners</title></book>
<book id="2"><title>Advanced XML</title></book>
</books>
XML;
$doc = new DOMDocument();
$doc->formatOutput = true;
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
// Rename the first title
$first = $xpath->query('//book[@id="1"]/title')->item(0);
$first->nodeValue = 'PHP Essentials';
// Remove the second book
$second = $xpath->query('//book[@id="2"]')->item(0);
$second->parentNode->removeChild($second);
echo $doc->saveXML();Вывод:
<?xml version="1.0"?>
<books>
<book id="1"><title>PHP Essentials</title></book>
</books>Чтобы удалить узел, нужно вызвать removeChild() на его родителе — именно поэтому $second->parentNode->removeChild($second) является стандартным идиоматическим способом. Присвоение значения nodeValue заменяет текстовое содержимое элемента.
Запросы с помощью XPath
DOMXPath позволяет выбирать узлы с помощью выражений пути вместо ручного обхода дерева — что значительно лаконичнее вложенных циклов.
<?php
$xml = <<<XML
<books>
<book id="1"><title>PHP for Beginners</title></book>
<book id="2"><title>Advanced XML</title></book>
</books>
XML;
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
// Every <title> anywhere in the document
foreach ($xpath->query('//title') as $node) {
echo $node->nodeValue . "\n";
}
// The title of the book whose id is "2"
$result = $xpath->query('//book[@id="2"]/title');
echo "Book 2: " . $result->item(0)->nodeValue . "\n";Вывод:
PHP for Beginners
Advanced XML
Book 2: Advanced XMLРаспространённые шаблоны XPath:
| Выражение | Выбирает |
|---|---|
//title | Все элементы <title> на любой глубине |
/books/book | Дочерние элементы <book> непосредственно под корнем <books> |
//book[@id="2"] | Элементы <book> с атрибутом id="2" |
//book/@id | Узлы атрибута id каждого <book> |
//book[1] | Первый <book> (индексы XPath начинаются с 1) |
Сохранение в файл
Сохраните дерево из памяти с помощью save(), который возвращает false при ошибке (например, при недоступном для записи каталоге).
<?php
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->formatOutput = true;
$root = $doc->createElement('config');
$doc->appendChild($root);
$root->appendChild($doc->createElement('debug', 'false'));
if ($doc->save(__DIR__ . '/output.xml')) {
echo 'Saved successfully.';
} else {
echo 'Failed to save — check directory permissions.';
}Используйте saveXML() (без аргументов), когда нужно получить XML в виде строки — это удобно для возврата из API или вывода в ответе.
Итоги
DOMDocumentзагружает весь XML-документ в изменяемое дерево узлов.- Используйте
loadXML()/load()для чтения,createElement()иappendChild()для построения,removeChild()для удаления. DOMXPath::query()выбирает узлы с помощью лаконичных выражений пути.save()записывает в файл;saveXML()возвращает документ в виде строки.- Выбирайте DOM, когда нужно записывать или запрашивать XML; выбирайте SimpleXML для быстрого чтения и XML Parser для потоковой обработки больших файлов.