Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 123 · От основ к полному API

DOM: полный справочник XML- и HTML-дерева, классов и методов

От первого XML-элемента до XPath, пространств имён, изменения дерева, HTML5, валидации и двух полных семейств DOM API.

DOM: открыть файл товара, прочитать поле, изменить и сохранить

Наглядная модель

Один товар: из XML-строки в дерево и обратно

В примере меняем описание товара, сохраняя атрибут stock. Документ, элемент, атрибут и текст — разные части одной структуры.

  1. XML поставщика

    <product stock="4">Чай</product> — пока обычная строка байтов.

  2. Document → Element product

    createFromString строит дерево. product имеет Attr stock=4 и дочерний Text Чай.

  3. Прочитать и изменить

    getAttribute("stock") возвращает строку 4. textContent="Кофе" заменяет текст у того же product.

  4. XML для получателя

    saveXml(product) возвращает <product stock="4">Кофе</product>. Файл и база сами не изменились.

Дерево не является набором независимых строк: две переменные могут ссылаться на один узел. Для копии нужен clone/import; для сохранения — отдельная операция. DOM хранит документ целиком в памяти.

Поставщик прислал product.xml. Начнём с малого: получим название Desk. Затем изменим остаток, обработаем несколько товаров и сохраним отдельную выгрузку. DOM хранит документ как дерево: документ владеет всем деревом, элемент product обозначает товар, атрибуты sku/stock — его именованные поля, а текстовый узел содержит Desk.

Исходный файл product.xml
<product sku="A1" stock="4">Desk</product>

В примере нужен PHP 8.4 или новее. Dom\XMLDocument — класс XML-документа. ::createFromFile(...) вызывает его фабрику: она читает путь и возвращает созданный документ. $document = сохраняет его в переменную. documentElement — корневой элемент, textContent — текст внутри него; стрелка читает свойство объекта. echo показывает значение, "\n" добавляет перевод строки.

Две строки PHP → название DeskСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromFile(__DIR__ . '/product.xml');
echo $document->documentElement->textContent, "\n";
Ожидаемый результат
Desk

__DIR__ обозначает папку текущего PHP-файла; точка соединяет её с именем product.xml. Файл входит в архив, его содержимое показано выше. Мы прочитали дерево, исходный файл пока не меняли.

Что хранится в памяти
  1. Document — всё содержимое product.xml
  2. documentElement — Element product
  3. getAttribute("stock") — строка "4"
  4. Text внутри product — слово Desk
  5. saveXml собирает строку XML из текущего дерева

Теперь тот же принцип на короткой строке: прочитаем остаток и заменим название. Создание дерева, изменение узла и сериализация — отдельные действия. Сериализация означает превращение дерева обратно в текст XML; она не равна записи в файл.

Запись в первом примереЧто она делает
$xml = '...';Сохраняет XML-текст в переменную; точка с запятой завершает инструкцию.
Dom\XMLDocument::createFromString($xml)Статическая фабрика класса получает строку и возвращает новый документ. :: выбирает метод класса.
$document->documentElementСтрелка -> читает корневой Element объекта document; без скобок это свойство.
$product->getAttribute("stock")Скобки вызывают метод с одним аргументом — именем поля.
$product->textContent = "Кофе"Присваивает буквальный текст содержимому элемента; старые дети заменяются.
echo $document->saveXml($product);Сериализует выбранный узел в строку и выводит её.
Прочитать и изменить один товарСамостоятельный пример
<?php
declare(strict_types=1);

$xml = '<product stock="4">Чай</product>';
$document = Dom\XMLDocument::createFromString($xml);
$product = $document->documentElement;
echo $product->getAttribute("stock"), ":", $product->textContent, "
";
$product->textContent = "Кофе";
echo $document->saveXml($product), "
";
Ожидаемый результат
4:Чай
<product stock="4">Кофе</product>

Товар изменён только в памяти; файл/база поставщика не менялись. Остаток пока строка, правила количества проверяются отдельно.

DOM похож на дерево папок, но атрибут — поле элемента, а не его дочерняя папка. У узла один родитель, поэтому обычная вставка уже существующего узла переносит его. Чтобы получить независимую копию, нужны clone/import. Две PHP-переменные могут указывать на один объект. Для простого чтения маленького XML короче SimpleXML; для последовательного большого файла — XMLReader.

Установка, версии и выбор семейства DOM

Расширение dom требует libxml и по умолчанию включено при сборке PHP. Отключение — --disable-dom. Исторические флаги libxml до PHP7.4 и нынешняя конфигурация сборки описаны в разделе libxml. На готовом сервере включение/пакет зависят от дистрибутива; проверяйте именно PHP веб-сервера, а не только отдельный CLI. Собственных параметров настройки dom в php.ini нет, основные настройки задаются свойствами документов и аргументами методов.

СемействоКогда выбирать
DOMDocument, DOMElement, DOMXPathСтарые библиотеки/совместимость. Не добавляет автоматически современный HTML5 parser или querySelector после обновления PHP.
Dom\XMLDocument / Dom\HTMLDocument, Dom\Element, Dom\XPathPHP8.4+, современный интерфейс. Явный выбор XML/HTML parser, стандартные namespace-правила.
PHP8.5 дополненияchildren, outerHTML, getElementsByClassName; запрещён clone коллекций. Все точные сигнатуры сверены с PHP8.5.11.
libxml2.11.9 стендаВерсия движка независима от PHP; отдельные новые флаги могут отсутствовать. Не объявляем любой PHP8.5 равным libxml2.13+.
Понять, какие варианты DOM доступны в вашем PHPСамостоятельный пример
<?php
declare(strict_types=1);

echo extension_loaded('dom') ? "XML-дерево: расширение dom включено\n" : "Включите dom\n";
echo class_exists(DOMDocument::class) ? "DOMDocument: совместимые библиотеки доступны\n" : "DOMDocument недоступен\n";
echo class_exists(Dom\HTMLDocument::class) ? "HTML5: современный разбор доступен\n" : "Для современного HTML нужен PHP 8.4+\n";
Ожидаемый результат
XML-дерево: расширение dom включено
DOMDocument: совместимые библиотеки доступны
HTML5: современный разбор доступен

Двоеточие ::class даёт строку с именем класса; class_exists проверяет его наличие. Результат нашей PHP8.5.11-сборки не обещает тот же набор у вашего веб-сервера. Проверка наличия не загружает XML и не создаёт страницу.

Старые и новые узлы не взаимозаменяемы. Для перехода из legacy есть Dom\Document::importLegacyNode. Не переименовывайте класс в use и не считайте миграцию завершённой: различаются null/false, namespace, HTML-разбор и доступные методы.

Один товар → каталог с повторяющимися product

Следующее требование — список остатков по всем товарам. В catalog.xml корневой catalog содержит несколько product. Каждый product имеет атрибут sku и дочерние элементы name/stock. Это отличается от первого файла: название теперь вложено в name, поэтому textContent целого product склеил бы название и остаток.

Исходный файл catalog.xml
<catalog>
  <product sku="A1"><name>Desk</name><stock>3</stock></product>
  <product sku="B2"><name>Lamp</name><stock>0</stock></product>
</catalog>
Прочитать SKU, название и остаток двух товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromFile(__DIR__ . '/catalog.xml');
foreach ($document->getElementsByTagName('product') as $product) {
    $name = $product->getElementsByTagName('name')->item(0)->textContent;
    $stock = $product->getElementsByTagName('stock')->item(0)->textContent;
    echo $product->getAttribute('sku'), ': ', $name, ' — ', $stock, "\n";
}
Ожидаемый результат
A1: Desk — 3
B2: Lamp — 0

getElementsByTagName возвращает коллекцию совпадений. foreach последовательно подставляет каждый товар в $product. В каждом товаре item(0) выбирает первый name/stock: PHP-индексация коллекций начинается с 0. В этом согласованном файле поля есть; произвольный вход требует проверки структуры до такого доступа.

Это поиск всех потомков, а не только прямых детей. Если во входе бывают вложенные товары или словари namespace, задавайте точную структуру через XPath или проверяйте XSD. XPath — язык путей по дереву; его минимальные обозначения разобраны в карточке XPath.

Изменённый узел → новый файл product-updated.xml

Поменяем остаток с 4 на 6. setAttribute принимает два аргумента: имя поля и новое строковое значение. saveXmlFile получает путь результата; исходный product.xml останется доступен отдельно. Проверка === false обнаруживает отказ записи.

Изменить товар, записать файл и показать его байтыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromFile(__DIR__ . '/product.xml');
$document->documentElement->setAttribute('stock', '6');
$path = __DIR__ . '/product-updated.xml';
if ($document->saveXmlFile($path) === false) { throw new RuntimeException('Файл не записан'); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8"?>
<product sku="A1" stock="6">Desk</product>

file_get_contents читает именно сохранённый файл. XML-декларация описывает формат и кодировку, а не версию PHP. Современный сериализатор не обязан добавлять финальный перевод строки. Для сохранения только строки используйте saveXml; для записи в другой интеграционный файл — saveXmlFile.

Выбрать инструмент под задачу

ЗадачаПодход
Прочитать одно поле и изменить небольшое деревоDom\XMLDocument::createFromString → Element/Attr → saveXml.
Найти узлы по структуре/условиюDom\XPath::query; для CSS Dom\Document::querySelectorAll.
Обработать современный HTMLDom\HTMLDocument::createFromString; PHP не выполняет браузерный JavaScript.
Копировать между документамиDom\Document::importNode; для переноса того же узла Dom\Document::adoptNode.
Проверить формат поставщикаDom\Document::schemaValidateSource плюс собственная проверка значений/прав.
Гигабайтный последовательный XMLXMLReader вместо полного дерева; для потокового вывода XMLWriter.
Лёгкое чтение небольшого XMLSimpleXML; мост к DOM доступен без повторной сериализации.

DOM расходует память на всё дерево и native структуры libxml. Размер файла не равен расходу памяти; memory_get_usage не обязательно показывает все native выделения. Не обещаем «всегда быстрее regex/SimpleXML»: измеряйте нужную операцию на своих размерах, сохраняя одинаковый результат. Сетевой ввод, schema imports, повторные XPath, глубокие копии и сериализация большого дерева — отдельные расходы.

DOMCharacterData: строка внутри узла

В подписи, комментарии или CDATA хранится строка одного узла. Общие свойства таких узлов описывает CharacterData: data —сама строка, length —её длина в кодовых точках. Нужный Text обычно получают через documentElement->firstChild после разбора простого элемента с текстом; для нового текста вызывают createTextNode у документа. Здесь сначала разбираем A😀Б: три кодовые точки занимают семь байтов UTF-8, поэтому байтовые позиции strlen не подходят для методов CharacterData. В новом DOM эта общая часть есть также у ProcessingInstruction. Текстовый узел не становится контейнером элементов от того, что его строка похожа на разметку.

СвойствоПрактический смысл
data: string, чтение/записьСодержимое одного узла UTF-8. Присваивание заменяет строку без разбора тегов.
length: int, только чтениеКоличество кодовых точек, не strlen в байтах и не число графем. Значение меняется вслед за data.
previousElementSibling / nextElementSibling: ?ElementБлижайшие соседние элементы: текст и комментарии между ними пропускаются. Это не previousSibling/nextSibling, которые возвращают любой тип узла.
Текстовый узел и его строка
  1. label — элемент
  2. внутри label один Text с data="A😀Б"
  3. length=3, strlen(data)=7: два разных измерения
Сравнить число кодовых точек с байтами UTF-8Самостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>A😀Б</label>');
$text = $document->documentElement->firstChild;
echo $text->length, ':', strlen($text->data), "
";
echo $text->substringData(1, 1), "
";
Ожидаемый результат
3:7
😀

В PHP DOM emoji занимает одну кодовую точку. Если позиция пришла из JavaScript, сначала согласуйте единицы индекса, особенно для символов за пределами BMP.

Проверить невалидную начальную позициюСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>AB</label>');
$text = $document->documentElement->firstChild;
try {
    $text->substringData(3, 1);
} catch (DOMException $error) {
    echo $error->code === DOM_INDEX_SIZE_ERR ? "range error
" : "other error
";
}
echo $text->data, "
";
Ожидаемый результат
range error
AB

После отклонённой операции чтения строка не изменилась. Координаты выделения из пользовательского интерфейса проверяйте отдельно, до вызова API.

Свойство этого классаЗначение и границы
$data: stringТекст CharacterData/ProcessingInstruction. Изменяемый; индексы методов CharacterData в PHP считают Unicode code points UTF-8, не байты.
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.
$previousElementSibling: ?DOMElementТолько чтение. Предыдущий сосед-Element, пропуская Text/Comment; null если нет.
$nextElementSibling: ?DOMElementТолько чтение. Следующий сосед-Element, пропуская Text/Comment; null если нет.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMCharacterData::appendData, DOMCharacterData::substringData, DOMCharacterData::insertData, DOMCharacterData::deleteData, DOMCharacterData::replaceData, DOMCharacterData::replaceWith, DOMCharacterData::remove, DOMCharacterData::before, DOMCharacterData::after.

Интерфейсы: DOMChildNode.

Официальное описание DOMCharacterData; точные современные сигнатуры сверены с PHP 8.5.11.

Прочитать остаток и пустой диапазон, не удаляя данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Привет</label>');
$text = $document->documentElement->firstChild;
echo 'С позиции 3: [', $text->substringData(3, 99), "]\n";
echo 'Ноль символов: [', $text->substringData(2, 0), "]\n";
echo 'С конца: [', $text->substringData($text->length, 2), "]\n";
echo 'Полная подпись: ', $text->data, "\n";
Ожидаемый результат
С позиции 3: [вет]
Ноль символов: []
С конца: []
Полная подпись: Привет

Индекс3 у буквы в; count99 ограничивается оставшимися тремя буквами. Count0 возвращает пустую строку. Offset=length разрешён и даёт пустой остаток. Квадратные скобки напечатаны вокруг возвращённых строк, чтобы пустой результат был виден. Ни один из вызовов не меняет data; offset больше length уже ошибка из предыдущего примера.

Из текста между элементами перейти к соседним полямСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<line><code>A</code> / <!-- separator --><name>Book</name></line>');
$separator = $document->documentElement->firstChild->nextSibling;
echo 'Сам Text: [', $separator->data, "]\n";
echo 'Любой следующий узел: ', $separator->nextSibling->nodeName, "\n";
echo 'Элемент слева: ', $separator->previousElementSibling->textContent, "\n";
echo 'Элемент справа: ', $separator->nextElementSibling->textContent, "\n";
Ожидаемый результат
Сам Text: [ / ]
Любой следующий узел: #comment
Элемент слева: A
Элемент справа: Book

NextSibling от code выбирает Text-разделитель. Его следующий любой узел —комментарий, а nextElementSibling пропускает комментарий и находит name. PreviousElementSibling находит code. Так можно ориентироваться рядом с текстом, не принимая форматирующие узлы за поля данных; на краю дерева соответствующее свойство вернуло бы null.

DOMText: текст, хвост и wholeText

Text хранит буквальное содержимое элемента: для подписи Book это отдельный узел внутри label. В нём доступны data и методы CharacterData; splitText возвращает отделённый хвост для последующей вставки в strong. Свойство wholeText доступно только для чтения: оно объединяет соседние Text и CDATA, не меняя структуру. Группа заканчивается на Element, Comment или ProcessingInstruction. Ниже видно различие с textContent всего родителя: комментарий разрывает группу wholeText, а общий текст label читается по обе стороны. Современный Dom\Text создают через createTextNode у документа; старый DOMText также имеет конструктор.

wholeText читает соседние фрагменты
  1. Text("A") → Text("B") → Comment → Text("C")
  2. У первого Text wholeText="AB"
  3. Сами два узла остаются двумя; normalize меняет дерево отдельно
Прочитать соседний текст и увидеть границу комментарияСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label/>');
$label = $document->documentElement;
$first = $label->appendChild($document->createTextNode('A'));
$label->appendChild($document->createTextNode('B'));
$label->appendChild($document->createComment('boundary'));
$label->appendChild($document->createTextNode('C'));
echo 'Соседняя группа: ', $first->wholeText, "\n";
echo 'Текст всего label: ', $label->textContent, "\n";
echo 'XML: ', $document->saveXML($label), "\n";
echo 'Узлов после чтения: ', $label->childNodes->length, "\n";
Ожидаемый результат
Соседняя группа: AB
Текст всего label: ABC
XML: <label>AB<!--boundary-->C</label>
Узлов после чтения: 4

Два appendChild создали соседние Text A и B. WholeText у первого читает AB до комментария; textContent родителя читает ABC, исключая текст комментария. Чтение wholeText не склеило узлы: всё ещё четыре ребёнка. SaveXML показывает расположение границы, но соседние Text A/B записывает подряд; XML-строка одна не раскрывает их внутреннее количество.

Не реализованный старый DOMText::replaceWholeText удалён в PHP 8.0. Выбирайте явную замену нужных узлов; не добавляйте вызов отсутствующего метода из старой заметки.

Свойство этого классаЗначение и границы
$wholeText: stringТолько чтение. Склеенный текст логически соседних Text/CDATA; само дерево не меняет. Для мутации legacy replaceWholeText, для нормализации normalize.

Унаследованные свойства и операции: DOMCharacterData. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMText::__construct, DOMText::isWhitespaceInElementContent, DOMText::isElementContentWhitespace, DOMText::splitText.

Интерфейсы: DOMChildNode.

Официальное описание DOMText; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\CharacterData: строка внутри узла

В подписи, комментарии или CDATA хранится строка одного узла. Общие свойства таких узлов описывает CharacterData: data —сама строка, length —её длина в кодовых точках. Нужный Text обычно получают через documentElement->firstChild после разбора простого элемента с текстом; для нового текста вызывают createTextNode у документа. Здесь сначала разбираем A😀Б: три кодовые точки занимают семь байтов UTF-8, поэтому байтовые позиции strlen не подходят для методов CharacterData. В новом DOM эта общая часть есть также у ProcessingInstruction. Текстовый узел не становится контейнером элементов от того, что его строка похожа на разметку.

СвойствоПрактический смысл
data: string, чтение/записьСодержимое одного узла UTF-8. Присваивание заменяет строку без разбора тегов.
length: int, только чтениеКоличество кодовых точек, не strlen в байтах и не число графем. Значение меняется вслед за data.
previousElementSibling / nextElementSibling: ?ElementБлижайшие соседние элементы: текст и комментарии между ними пропускаются. Это не previousSibling/nextSibling, которые возвращают любой тип узла.
Текстовый узел и его строка
  1. label — элемент
  2. внутри label один Text с data="A😀Б"
  3. length=3, strlen(data)=7: два разных измерения
Сравнить число кодовых точек с байтами UTF-8Самостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>A😀Б</label>');
$text = $document->documentElement->firstChild;
echo $text->length, ':', strlen($text->data), "
";
echo $text->substringData(1, 1), "
";
Ожидаемый результат
3:7
😀

В PHP DOM emoji занимает одну кодовую точку. Если позиция пришла из JavaScript, сначала согласуйте единицы индекса, особенно для символов за пределами BMP.

Проверить невалидную начальную позициюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>AB</label>');
$text = $document->documentElement->firstChild;
try {
    $text->substringData(3, 1);
} catch (DOMException $error) {
    echo $error->code === DOM_INDEX_SIZE_ERR ? "range error
" : "other error
";
}
echo $text->data, "
";
Ожидаемый результат
range error
AB

После отклонённой операции чтения строка не изменилась. Координаты выделения из пользовательского интерфейса проверяйте отдельно, до вызова API.

Свойство этого классаЗначение и границы
$previousElementSibling: ?Dom\ElementТолько чтение. Предыдущий сосед-Element, пропуская Text/Comment; null если нет.
$nextElementSibling: ?Dom\ElementТолько чтение. Следующий сосед-Element, пропуская Text/Comment; null если нет.
$data: stringТекст CharacterData/ProcessingInstruction. Изменяемый; индексы методов CharacterData в PHP считают Unicode code points UTF-8, не байты.
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\CharacterData::substringData, Dom\CharacterData::appendData, Dom\CharacterData::insertData, Dom\CharacterData::deleteData, Dom\CharacterData::replaceData, Dom\CharacterData::remove, Dom\CharacterData::before, Dom\CharacterData::after, Dom\CharacterData::replaceWith.

Интерфейсы: Dom\ChildNode.

Официальное описание Dom\CharacterData; точные современные сигнатуры сверены с PHP 8.5.11.

Прочитать остаток и пустой диапазон, не удаляя данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Привет</label>');
$text = $document->documentElement->firstChild;
echo 'С позиции 3: [', $text->substringData(3, 99), "]\n";
echo 'Ноль символов: [', $text->substringData(2, 0), "]\n";
echo 'С конца: [', $text->substringData($text->length, 2), "]\n";
echo 'Полная подпись: ', $text->data, "\n";
Ожидаемый результат
С позиции 3: [вет]
Ноль символов: []
С конца: []
Полная подпись: Привет

Индекс3 у буквы в; count99 ограничивается оставшимися тремя буквами. Count0 возвращает пустую строку. Offset=length разрешён и даёт пустой остаток. Квадратные скобки напечатаны вокруг возвращённых строк, чтобы пустой результат был виден. Ни один из вызовов не меняет data; offset больше length уже ошибка из предыдущего примера.

Из текста между элементами перейти к соседним полямСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<line><code>A</code> / <!-- separator --><name>Book</name></line>');
$separator = $document->documentElement->firstChild->nextSibling;
echo 'Сам Text: [', $separator->data, "]\n";
echo 'Любой следующий узел: ', $separator->nextSibling->nodeName, "\n";
echo 'Элемент слева: ', $separator->previousElementSibling->textContent, "\n";
echo 'Элемент справа: ', $separator->nextElementSibling->textContent, "\n";
Ожидаемый результат
Сам Text: [ / ]
Любой следующий узел: #comment
Элемент слева: A
Элемент справа: Book

NextSibling от code выбирает Text-разделитель. Его следующий любой узел —комментарий, а nextElementSibling пропускает комментарий и находит name. PreviousElementSibling находит code. Так можно ориентироваться рядом с текстом, не принимая форматирующие узлы за поля данных; на краю дерева соответствующее свойство вернуло бы null.

Dom\Text: текст, хвост и wholeText

Text хранит буквальное содержимое элемента: для подписи Book это отдельный узел внутри label. В нём доступны data и методы CharacterData; splitText возвращает отделённый хвост для последующей вставки в strong. Свойство wholeText доступно только для чтения: оно объединяет соседние Text и CDATA, не меняя структуру. Группа заканчивается на Element, Comment или ProcessingInstruction. Ниже видно различие с textContent всего родителя: комментарий разрывает группу wholeText, а общий текст label читается по обе стороны. Современный Dom\Text создают через createTextNode у документа; старый DOMText также имеет конструктор.

wholeText читает соседние фрагменты
  1. Text("A") → Text("B") → Comment → Text("C")
  2. У первого Text wholeText="AB"
  3. Сами два узла остаются двумя; normalize меняет дерево отдельно
Прочитать соседний текст и увидеть границу комментарияСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label/>');
$label = $document->documentElement;
$first = $label->appendChild($document->createTextNode('A'));
$label->appendChild($document->createTextNode('B'));
$label->appendChild($document->createComment('boundary'));
$label->appendChild($document->createTextNode('C'));
echo 'Соседняя группа: ', $first->wholeText, "\n";
echo 'Текст всего label: ', $label->textContent, "\n";
echo 'XML: ', $document->saveXML($label), "\n";
echo 'Узлов после чтения: ', $label->childNodes->length, "\n";
Ожидаемый результат
Соседняя группа: AB
Текст всего label: ABC
XML: <label>AB<!--boundary-->C</label>
Узлов после чтения: 4

Два appendChild создали соседние Text A и B. WholeText у первого читает AB до комментария; textContent родителя читает ABC, исключая текст комментария. Чтение wholeText не склеило узлы: всё ещё четыре ребёнка. SaveXML показывает расположение границы, но соседние Text A/B записывает подряд; XML-строка одна не раскрывает их внутреннее количество.

Не реализованный старый DOMText::replaceWholeText удалён в PHP 8.0. Выбирайте явную замену нужных узлов; не добавляйте вызов отсутствующего метода из старой заметки.

Свойство этого классаЗначение и границы
$wholeText: stringТолько чтение. Склеенный текст логически соседних Text/CDATA; само дерево не меняет. Для мутации legacy replaceWholeText, для нормализации normalize.

Унаследованные свойства и операции: Dom\CharacterData. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\Text::splitText.

Интерфейсы: Dom\ChildNode.

Официальное описание Dom\Text; точные современные сигнатуры сверены с PHP 8.5.11.

DOMChildNode: менять место узла через сам узел

В редакторе иногда нужно удалить placeholder, не различая Text и Element. ChildNode описывает операции самого ребёнка: before/after добавляют соседей, replaceWith заменяет его место, remove разрывает связь с родителем. Интерфейс не создают через new; его реализуют DOMElement и DOMCharacterData. В функции ниже параметр placeholder получает конкретный Text Temporary из места вызова. Тело вызывает remove; return void возвращает выполнение к следующему echo, который показывает label с сохранённой ценой100. Для иного типа объекта обещание интерфейса не отменяет структурных ограничений, например защиты DocumentType.

Один контракт для разных узлов
  1. Функция получает ChildNode
  2. Вызывает remove() у элемента или текстового узла
  3. Конкретный объект разрывает собственную связь с родителем
Удалить placeholder через интерфейсСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Temporary<price>100</price></label>');
function removePlaceholder(DOMChildNode $placeholder): void {
    $placeholder->remove();
}
removePlaceholder($document->documentElement->firstChild);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label><price>100</price></label>

Тип параметра говорит о доступной операции. Вызов не обязан знать, что placeholder оказался Text. Это конкретный пример пользы интерфейса, а не требование оборачивать в функцию каждую строку.

Для вставки внутрь контейнера нужен другой контракт — DOMParentNode. У элемента могут быть обе роли одновременно.

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Все методы этого класса: DOMChildNode::remove, DOMChildNode::before, DOMChildNode::after, DOMChildNode::replaceWith.

Официальное описание DOMChildNode; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\ChildNode: менять место узла через сам узел

В редакторе иногда нужно удалить placeholder, не различая Text и Element. ChildNode описывает операции самого ребёнка: before/after добавляют соседей, replaceWith заменяет его место, remove разрывает связь с родителем. Интерфейс не создают через new; его реализуют Element, CharacterData и DocumentType. В функции ниже параметр placeholder получает конкретный Text Temporary из места вызова. Тело вызывает remove; return void возвращает выполнение к следующему echo, который показывает label с сохранённой ценой100. Для иного типа объекта обещание интерфейса не отменяет структурных ограничений, например защиты DocumentType.

Один контракт для разных узлов
  1. Функция получает ChildNode
  2. Вызывает remove() у элемента или текстового узла
  3. Конкретный объект разрывает собственную связь с родителем
Удалить placeholder через интерфейсСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Temporary<price>100</price></label>');
function removePlaceholder(Dom\ChildNode $placeholder): void {
    $placeholder->remove();
}
removePlaceholder($document->documentElement->firstChild);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label><price>100</price></label>

Тип параметра говорит о доступной операции. Вызов не обязан знать, что placeholder оказался Text. Это конкретный пример пользы интерфейса, а не требование оборачивать в функцию каждую строку.

Для вставки внутрь контейнера нужен другой контракт — Dom\ParentNode. У элемента могут быть обе роли одновременно.

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Все методы этого класса: Dom\ChildNode::remove, Dom\ChildNode::before, Dom\ChildNode::after, Dom\ChildNode::replaceWith.

Официальное описание Dom\ChildNode; точные современные сигнатуры сверены с PHP 8.5.11.

DOMParentNode: контейнер для детей

Нужно заполнить выбранный контейнер текстовыми подписями. ParentNode объединяет методы изменения детей: append добавляет в конец, prepend —в начало, replaceChildren заменяет весь набор. В вызове ниже catalog передаётся в параметр container, массив [A,B] —в labels. Запись ...$labels раскрывает два аргумента; replaceChildren превращает строки в два Text. Функция возвращает void, затем echo читает AB из изменённого catalog. Интерфейс не обещает одинаковые допустимые дети у всех реализаций: документ, элемент и временный фрагмент имеют разные структурные правила. Поэтому эта функция подходит контейнеру, допускающему Text, а не корневому уровню современного Document.

Один контракт, разные контейнеры
  1. Element catalog → позиции и текст
  2. DocumentFragment → временная группа детей
  3. Document → корень, допустимые PI/комментарии/doctype
Заполнить элемент через контракт ParentNodeСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
function fillLabels(DOMParentNode $container, array $labels): void {
    $container->replaceChildren(...$labels);
}
fillLabels($document->documentElement, ['A', 'B']);
echo $document->documentElement->textContent, "
";
Ожидаемый результат
AB

Функция предназначена для контейнера, допускающего текст. Тип ParentNode описывает методы, а это дополнительное предусловие следует из задачи. Для документного корня функция не подходит.

Старый DOMParentNode содержит только append/prepend/replaceChildren; XPath доступен отдельным объектом DOMXPath.

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Все методы этого класса: DOMParentNode::append, DOMParentNode::prepend, DOMParentNode::replaceChildren.

Официальное описание DOMParentNode; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\ParentNode: контейнер для детей

Нужно заполнить выбранный контейнер текстовыми подписями. ParentNode объединяет методы изменения детей: append добавляет в конец, prepend —в начало, replaceChildren заменяет весь набор. В вызове ниже catalog передаётся в параметр container, массив [A,B] —в labels. Запись ...$labels раскрывает два аргумента; replaceChildren превращает строки в два Text. Функция возвращает void, затем echo читает AB из изменённого catalog. Интерфейс не обещает одинаковые допустимые дети у всех реализаций: документ, элемент и временный фрагмент имеют разные структурные правила. Поэтому эта функция подходит контейнеру, допускающему Text, а не корневому уровню современного Document.

Один контракт, разные контейнеры
  1. Element catalog → позиции и текст
  2. DocumentFragment → временная группа детей
  3. Document → корень, допустимые PI/комментарии/doctype
Заполнить элемент через контракт ParentNodeСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
function fillLabels(Dom\ParentNode $container, array $labels): void {
    $container->replaceChildren(...$labels);
}
fillLabels($document->documentElement, ['A', 'B']);
echo $document->documentElement->textContent, "
";
Ожидаемый результат
AB

Функция предназначена для контейнера, допускающего текст. Тип ParentNode описывает методы, а это дополнительное предусловие следует из задачи. Для документного корня функция не подходит.

В современном интерфейсе PHP 8.4+ есть также Dom\ParentNode::querySelector и Dom\ParentNode::querySelectorAll для поиска по CSS-селектору.

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Все методы этого класса: Dom\ParentNode::append, Dom\ParentNode::prepend, Dom\ParentNode::replaceChildren, Dom\ParentNode::querySelector, Dom\ParentNode::querySelectorAll.

Официальное описание Dom\ParentNode; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\TokenList: class как связанный набор

Нужно добавить карточке HTML метку featured, не потеряв card и не повторив уже существующее имя. Свойство element->classList даёт TokenList —связанный с class набор отдельных CSS-имён. Это не PHP-классы и не самостоятельный массив: add/remove сразу меняют тот же HTML-атрибут. Сначала создаём HTMLDocument, затем article и class; объект списка получаем чтением свойства, через new его не создают. ASCII-пробелы разделяют токены, повторения считаются один раз. Value читает сырую строку атрибута, тогда как item/count работают с уникальными именами. Ниже это видно по двум пробелам и повтору card, а затем по полной замене на compact selected.

СвойствоЧто происходит
length: int, только чтениеРазмер набора уникальных токенов.
value: string, чтение/записьСырая строка связанного атрибута class. Присваивание заменяет весь атрибут; чтение может сохранять исходные повторы/пробелы. NUL отклоняется ValueError.
Два представления одного атрибута
  1. element.class = "card featured card"
  2. classList.value читает строку, count/item — набор
  3. add/remove/toggle изменяют атрибут у того же element
Увидеть связь value и набораСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card  featured card');
$tokens = $element->classList;
echo $tokens->value, "
";
echo $tokens->count(), "
";
$tokens->value = 'compact selected';
echo $element->getAttribute('class'), "
";
Ожидаемый результат
card  featured card
2
compact selected

Полная замена value отличается от добавления одного токена. Существующие метки card/featured исчезли, потому что присвоена новая строка.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.
$value: stringУ Attr строковое значение, у TokenList весь class, у backed enum строковой ключ. Legacy DOMAttr.value подставляет сущности и особым образом трактует &: для буквальных данных Element::setAttribute. Modern Attr.value не подставляет сущности.

Все методы этого класса: Dom\TokenList::__construct, Dom\TokenList::item, Dom\TokenList::contains, Dom\TokenList::add, Dom\TokenList::remove, Dom\TokenList::toggle, Dom\TokenList::replace, Dom\TokenList::supports, Dom\TokenList::count, Dom\TokenList::getIterator.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание Dom\TokenList; точные современные сигнатуры сверены с PHP 8.5.11.

Отклонить неверный токен и добавить два класса отдельными аргументамиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
foreach (['', 'featured selected'] as $candidate) {
    try {
        $tokens->add($candidate);
    } catch (DOMException $error) {
        echo 'Токен отклонён; class остаётся: ', $tokens->value, "\n";
    }
}
$tokens->add('featured', 'selected');
echo 'После исправления: ', $document->saveHtml($element), "\n";
Ожидаемый результат
Токен отклонён; class остаётся: card
Токен отклонён; class остаётся: card
После исправления: <article class="card featured selected"></article>

Пустая строка нарушает синтаксис одного имени; строка с ASCII-пробелом пытается передать два имени как один токен. Оба отказа обработаны до дальнейшей работы. Исправление —два отдельных аргумента add, а не удаление пробела с последующей склейкой имён. Для данных формы сначала проверяют, разрешены ли эти имена правилами компонента.

Класс final: наследоваться от него нельзя. Он реализует Countable и IteratorAggregate; отсюда count и foreach. Для чтения одного токена доступна также запись с квадратными скобками. Это удобная форма доступа, а не превращение TokenList в PHP-массив; менять набор следует его методами или присваиванием value.

Прочитать токен квадратными скобками с запасной подписьюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card featured');
$tokens = $element->classList;
echo 'Первый: ', $tokens[0], "\n";
echo 'Третий: ', $tokens[2] ?? 'не задан', "\n";
$tokens->add('selected');
echo 'После добавления третий: ', $tokens[2], "\n";
Ожидаемый результат
Первый: card
Третий: не задан
После добавления третий: selected

Индекс0 выбирает card, как item(0). В начале индекс2 отсутствует; оператор ?? подставляет строку не задан для null. После add новый selected занял позицию2, и чтение тем же синтаксисом уже возвращает его. Индекс описывает порядок уникальных меток, не приоритет CSS.

DOMNodeList: список узлов и его связь с деревом

Нужно вывести или изменить несколько товаров XML-каталога. GetElementsByTagName(product) возвращает коллекцию найденных элементов. Такой список не является товаром: сначала item(0) выбирает узел, затем textContent читает его название. Сам DOM создаёт коллекцию, публичного конструктора из PHP-массива нет. Readonly length сообщает число позиций; индексы идут от0 до length-1. Countable даёт count, IteratorAggregate —foreach. NodeList может содержать также Text, Comment и другие типы; состав определяется источником списка. Полученный объект хранит доступ к узлам, а не копию их строковых данных.

Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea

«Живой» означает, что изменение дерева отражается в списке. Это определяется источником: childNodes и getElementsByTagName живые; результат XPath и querySelectorAll — снимок набора. Даже в снимке узлы остаются связанными с документом. Нельзя переносить слово «статический» из краткого описания класса на все создаваемые им коллекции.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Все методы этого класса: DOMNodeList::count, DOMNodeList::getIterator, DOMNodeList::item.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание DOMNodeList; точные современные сигнатуры сверены с PHP 8.5.11.

Изменить найденный объект и увидеть правку в каталогеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$first = $items[0];
if ($first !== null) {
    $first->textContent = 'Green tea';
}
echo $document->saveXml($document->documentElement), "\n";
echo 'Через item(0): ', $items->item(0)->textContent, "\n";
Ожидаемый результат
<catalog><product id="p1">Green tea</product><product id="p2">Coffee</product></catalog>
Через item(0): Green tea

Квадратные скобки дают индексный доступ к узлу, а не к тексту. First указывает на product внутри документа; присваивание меняет именно его. Item(0) читает тот же изменённый узел. Для отделённой копии содержимого нужен cloneNode(true), для отдельного списка ссылок —iteratorToArray. Коллекцию нельзя clone начиная сPHP8.5.

Dom\NodeList: список узлов и его связь с деревом

Нужно вывести или изменить несколько товаров XML-каталога. Свойство childNodes корневого catalog возвращает список непосредственных детей. В этой строке XML между product нет пробельных Text. Такой список не является товаром: сначала item(0) выбирает узел, затем textContent читает его название. Сам DOM создаёт коллекцию, публичного конструктора из PHP-массива нет. Readonly length сообщает число позиций; индексы идут от0 до length-1. Countable даёт count, IteratorAggregate —foreach. NodeList может содержать также Text, Comment и другие типы; состав определяется источником списка. Полученный объект хранит доступ к узлам, а не копию их строковых данных.

Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea

«Живой» означает, что изменение дерева отражается в списке. Это определяется источником: childNodes и getElementsByTagName живые; результат XPath и querySelectorAll — снимок набора. Даже в снимке узлы остаются связанными с документом. Нельзя переносить слово «статический» из краткого описания класса на все создаваемые им коллекции.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Все методы этого класса: Dom\NodeList::count, Dom\NodeList::getIterator, Dom\NodeList::item.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание Dom\NodeList; точные современные сигнатуры сверены с PHP 8.5.11.

Изменить найденный объект и увидеть правку в каталогеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
$first = $items[0];
if ($first !== null) {
    $first->textContent = 'Green tea';
}
echo $document->saveXml($document->documentElement), "\n";
echo 'Через item(0): ', $items->item(0)->textContent, "\n";
Ожидаемый результат
<catalog><product id="p1">Green tea</product><product id="p2">Coffee</product></catalog>
Через item(0): Green tea

Квадратные скобки дают индексный доступ к узлу, а не к тексту. First указывает на product внутри документа; присваивание меняет именно его. Item(0) читает тот же изменённый узел. Для отделённой копии содержимого нужен cloneNode(true), для отдельного списка ссылок —iteratorToArray. Коллекцию нельзя clone начиная сPHP8.5.

Dom\HTMLCollection: список узлов и его связь с деревом

Нужно вывести или изменить несколько товаров XML-каталога. GetElementsByTagName(product) возвращает коллекцию найденных элементов. Такой список не является товаром: сначала item(0) выбирает узел, затем textContent читает его название. Сам DOM создаёт коллекцию, публичного конструктора из PHP-массива нет. Readonly length сообщает число позиций; индексы идут от0 до length-1. Countable даёт count, IteratorAggregate —foreach. HTMLCollection содержит только Element, несмотря имя класса может быть получена и из XMLDocument. Полученный объект хранит доступ к узлам, а не копию их строковых данных.

Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea

«Живой» означает, что изменение дерева отражается в списке. Это определяется источником: childNodes и getElementsByTagName живые; результат XPath и querySelectorAll — снимок набора. Даже в снимке узлы остаются связанными с документом. Нельзя переносить слово «статический» из краткого описания класса на все создаваемые им коллекции.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Все методы этого класса: Dom\HTMLCollection::item, Dom\HTMLCollection::namedItem, Dom\HTMLCollection::count, Dom\HTMLCollection::getIterator.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание Dom\HTMLCollection; точные современные сигнатуры сверены с PHP 8.5.11.

Изменить найденный объект и увидеть правку в каталогеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$first = $items[0];
if ($first !== null) {
    $first->textContent = 'Green tea';
}
echo $document->saveXml($document->documentElement), "\n";
echo 'Через item(0): ', $items->item(0)->textContent, "\n";
Ожидаемый результат
<catalog><product id="p1">Green tea</product><product id="p2">Coffee</product></catalog>
Через item(0): Green tea

Квадратные скобки дают индексный доступ к узлу, а не к тексту. First указывает на product внутри документа; присваивание меняет именно его. Item(0) читает тот же изменённый узел. Для отделённой копии содержимого нужен cloneNode(true), для отдельного списка ссылок —iteratorToArray. Коллекцию нельзя clone начиная сPHP8.5.

DOMNamedNodeMap: именованные записи XML

Нужно получить атрибуты товара product и найти запись по имени. Element->attributes возвращает карту Attr. GetNamedItem(id) выбирает объект атрибута, а его nodeValue даёт строку p1. Поиск не создаёт новое поле. Readonly length сообщает размер, count и foreach обеспечиваются Countable и IteratorAggregate. Карта не является PHP-массивом; новые атрибуты записывают через Element. Современный DtdNamedNodeMap отделяет DTD-объявления от Dom\NamedNodeMap с атрибутами; старый DOMNamedNodeMap используется для обеих задач. Сначала выбирайте правильное свойство документа или элемента, затем правильный поиск внутри его карты.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Все методы этого класса: DOMNamedNodeMap::getNamedItem, DOMNamedNodeMap::getNamedItemNS, DOMNamedNodeMap::item, DOMNamedNodeMap::count, DOMNamedNodeMap::getIterator.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание DOMNamedNodeMap; точные современные сигнатуры сверены с PHP 8.5.11.

Изменить найденный Attr и сохранить прежнюю строку для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$stock = $items->getNamedItem('stock');
$before = $stock->nodeValue;
$stock->nodeValue = '3';
echo 'Было: ', $before, "\n";
echo 'Теперь у product: ', $document->documentElement->getAttribute('stock'), "\n";
echo 'Через карту: ', $items->getNamedItem('stock')->nodeValue, "\n";
Ожидаемый результат
Было: 4
Теперь у product: 3
Через карту: 3

Stock —тот же Attr, который принадлежит product; присваивание через найденный объект меняет исходный элемент. Before —скопированная строка4, поэтому она осталась прежней для журнала. Отдельный PHP-массив строк тоже сохраняет значения, а iteratorToArray карты сохранял бы ссылки на объекты.

У старого DOMNamedNodeMap с PHP8.0 удалены не реализованные setNamedItem, removeNamedItem, setNamedItemNS и removeNamedItemNS. Не переносите их из исторических примеров: используйте setAttribute/setAttributeNS и removeAttribute/removeAttributeNS у элемента. В PHP8.0 карта стала IteratorAggregate вместо прежнего Traversable. Для индексного чтения допустим синтаксис квадратных скобок; для смыслового поиска яснее getNamedItem.

Прочитать единственный атрибут квадратными скобкамиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1"/>');
$attributes = $document->documentElement->attributes;
$id = $attributes[0];
echo $id->nodeName, '=', $id->nodeValue, "\n";
$document->documentElement->setAttribute('stock', '3');
echo 'Новый атрибут через Element: ', $attributes->getNamedItem('stock')->nodeValue, "\n";
Ожидаемый результат
id=p1
Новый атрибут через Element: 3

В квадратных скобках указан индекс записи, не индекс символа p1. Для изменения состава карты обращаемся к владельцу Element: setAttribute создаёт stock. Уже сохранённая attributes видит новую запись; карта не стала самостоятельным массивом.

Dom\NamedNodeMap: именованные записи XML

Нужно получить атрибуты товара product и найти запись по имени. Element->attributes возвращает карту Attr. GetNamedItem(id) выбирает объект атрибута, а его nodeValue даёт строку p1. Поиск не создаёт новое поле. Readonly length сообщает размер, count и foreach обеспечиваются Countable и IteratorAggregate. Карта не является PHP-массивом; новые атрибуты записывают через Element. Современный DtdNamedNodeMap отделяет DTD-объявления от Dom\NamedNodeMap с атрибутами; старый DOMNamedNodeMap используется для обеих задач. Сначала выбирайте правильное свойство документа или элемента, затем правильный поиск внутри его карты.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Все методы этого класса: Dom\NamedNodeMap::item, Dom\NamedNodeMap::getNamedItem, Dom\NamedNodeMap::getNamedItemNS, Dom\NamedNodeMap::count, Dom\NamedNodeMap::getIterator.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание Dom\NamedNodeMap; точные современные сигнатуры сверены с PHP 8.5.11.

Изменить найденный Attr и сохранить прежнюю строку для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$stock = $items->getNamedItem('stock');
$before = $stock->nodeValue;
$stock->nodeValue = '3';
echo 'Было: ', $before, "\n";
echo 'Теперь у product: ', $document->documentElement->getAttribute('stock'), "\n";
echo 'Через карту: ', $items->getNamedItem('stock')->nodeValue, "\n";
Ожидаемый результат
Было: 4
Теперь у product: 3
Через карту: 3

Stock —тот же Attr, который принадлежит product; присваивание через найденный объект меняет исходный элемент. Before —скопированная строка4, поэтому она осталась прежней для журнала. Отдельный PHP-массив строк тоже сохраняет значения, а iteratorToArray карты сохранял бы ссылки на объекты.

Dom\DtdNamedNodeMap: именованные записи XML

Нужно получить объявления сущностей из внутренней DTD и найти запись по имени. Document->doctype->entities возвращает карту объявлений Entity; notations —карту объявлений Notation. Это объекты описания DTD, не позиции каталога. Readonly length сообщает размер, count и foreach обеспечиваются Countable и IteratorAggregate. Карта не является PHP-массивом; новые атрибуты записывают через Element. Современный DtdNamedNodeMap отделяет DTD-объявления от Dom\NamedNodeMap с атрибутами; старый DOMNamedNodeMap используется для обеих задач. Сначала выбирайте правильное свойство документа или элемента, затем правильный поиск внутри его карты.

Свойство этого классаЗначение и границы
$length: intТолько чтение. У CharacterData длина в Unicode code points; у коллекций число узлов/токенов. Не используйте единицы одной сущности для другой.

Все методы этого класса: Dom\DtdNamedNodeMap::item, Dom\DtdNamedNodeMap::getNamedItem, Dom\DtdNamedNodeMap::getNamedItemNS, Dom\DtdNamedNodeMap::count, Dom\DtdNamedNodeMap::getIterator.

Интерфейсы: IteratorAggregate, Traversable, Countable.

Официальное описание Dom\DtdNamedNodeMap; точные современные сигнатуры сверены с PHP 8.5.11.

DOMXPath: поисковый язык для дерева

XPath выражает путь от документа к узлам и условия выбора. Сначала отличайте query (узлы) от evaluate (значение выражения). Свойство document только читается; registerNodeNamespaces можно менять, но ручные префиксы обычно делают интеграцию стабильнее. Это XPath 1.0: операции над числами используют его числовую модель, поэтому денежные вычисления лучше выполнять отдельно в целых минимальных единицах.

Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число

В этом каталоге запрос product[@stock > 0] выберет Tea. Конструктор связывает поиск с деревом; query возвращает найденные узлы. Это конкретная задача языка XPath перед полным перечнем обозначений.

Создать поиск и получить доступный товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$available = $xpath->query('/catalog/product[@stock > 0]');
echo 'Можно заказать: ', $available->item(0)->textContent, "\n";
Ожидаемый результат
Можно заказать: Tea

new XPath связывает поисковый объект с документом. /catalog/product задаёт путь, @stock читает атрибут, [>0] отбирает доступную позицию. В этом фиксированном каталоге совпадение есть; для внешних данных проверяйте length и ошибки.

ЗаписьЧеловеческий смысл
/catalog/productТовары непосредственно внутри корня catalog
.//productВсе товары ниже переданного контекста
@stockАтрибут stock текущего элемента
[1]Первое совпадение в соответствующей XPath-оси; индексация начинается с 1
[last()]Последнее совпадение
string(...) / count(...) / sum(...)Получить текст, число узлов или сумму значений
c:productproduct в URI, зарегистрированном под префиксом c
Свойство этого классаЗначение и границы
$document: DOMDocumentТолько чтение. Readonly документ, к которому относится XPath. Другой документ требует другого XPath-объекта.
$registerNodeNamespaces: boolАвтоматическая регистрация префиксов контекста XPath. Можно изменить; явно зарегистрированные префиксы дают более предсказуемый контракт.

Все методы этого класса: DOMXPath::__construct, DOMXPath::evaluate, DOMXPath::query, DOMXPath::registerNamespace, DOMXPath::registerPhpFunctions, DOMXPath::registerPhpFunctionNS, DOMXPath::quote.

Официальное описание DOMXPath; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\XPath: поисковый язык для дерева

XPath выражает путь от документа к узлам и условия выбора. Сначала отличайте query (узлы) от evaluate (значение выражения). Свойство document только читается; registerNodeNamespaces можно менять, но ручные префиксы обычно делают интеграцию стабильнее. Это XPath 1.0: операции над числами используют его числовую модель, поэтому денежные вычисления лучше выполнять отдельно в целых минимальных единицах.

Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число

В этом каталоге запрос product[@stock > 0] выберет Tea. Конструктор связывает поиск с деревом; query возвращает найденные узлы. Это конкретная задача языка XPath перед полным перечнем обозначений.

Создать поиск и получить доступный товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$available = $xpath->query('/catalog/product[@stock > 0]');
echo 'Можно заказать: ', $available->item(0)->textContent, "\n";
Ожидаемый результат
Можно заказать: Tea

new XPath связывает поисковый объект с документом. /catalog/product задаёт путь, @stock читает атрибут, [>0] отбирает доступную позицию. В этом фиксированном каталоге совпадение есть; для внешних данных проверяйте length и ошибки.

ЗаписьЧеловеческий смысл
/catalog/productТовары непосредственно внутри корня catalog
.//productВсе товары ниже переданного контекста
@stockАтрибут stock текущего элемента
[1]Первое совпадение в соответствующей XPath-оси; индексация начинается с 1
[last()]Последнее совпадение
string(...) / count(...) / sum(...)Получить текст, число узлов или сумму значений
c:productproduct в URI, зарегистрированном под префиксом c
Свойство этого классаЗначение и границы
$document: Dom\DocumentТолько чтение. Readonly документ, к которому относится XPath. Другой документ требует другого XPath-объекта.
$registerNodeNamespaces: boolАвтоматическая регистрация префиксов контекста XPath. Можно изменить; явно зарегистрированные префиксы дают более предсказуемый контракт.

Все методы этого класса: Dom\XPath::__construct, Dom\XPath::evaluate, Dom\XPath::query, Dom\XPath::registerNamespace, Dom\XPath::registerPhpFunctions, Dom\XPath::registerPhpFunctionNS, Dom\XPath::quote.

Официальное описание Dom\XPath; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\AdjacentPosition: четыре именованные позиции

Положение выбирается относительно одного элемента: до его открывающего тега, в начале его содержимого, в конце содержимого либо после закрывающего тега. Enum заменяет свободную строку конкретным case. Это не координаты пикселей и не стили CSS.

Порядок вставки
  1. BeforeBegin | <product>
  2. AfterBegin → существующее содержимое → BeforeEnd
  3. </product> | AfterEnd

После выбора позиции BeforeEnd метка добавляется внутрь product в конец. Значения enum можно хранить строкой, но метод современного DOM принимает сам case.

Преобразовать настройку позиции и вставить меткуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product>Tea</product>');
$position = Dom\AdjacentPosition::from('beforeend');
$document->documentElement->insertAdjacentText($position, 'Sale: ');
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<product>TeaSale: </product>

Строка настройки превращается в допустимый enum. insertAdjacentText принимает этот case и вставляет буквальный текст в выбранную позицию. Для конца используйте текст с собственным начальным разделителем, например « — Sale».

Case / valueРодитель нового содержимого
BeforeBegin / beforebeginРодитель product; вставка перед product
AfterBegin / afterbeginСам product; первый ребёнок
BeforeEnd / beforeendСам product; последний ребёнок
AfterEnd / afterendРодитель product; вставка после product

DOMException: назначение, свойства и операции

DOMException сообщает нарушение правил дерева: чужой документ, недопустимое имя, не найденный ребёнок. Наследует обычный Exception; в modern API ошибки DOM по-прежнему представлены этим классом. Ловите её на границе операции и решайте, отклонить ли документ, исправить правило или вернуть понятную ошибку. Не подавляйте все ошибки, продолжая записывать частичный каталог.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMException в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMException представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Исключение DOM сообщает, что действие нарушило структуру дерева. Например, XML-документ может иметь один корневой элемент. try/catch позволяет объяснить отказ и прекратить нужную операцию, сохранив действительный документ.

Отклонить второй корень и сохранить понятный XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createEmpty();
$document->appendChild($document->createElement('catalog'));
try {
    $document->appendChild($document->createElement('order'));
} catch (DOMException $error) {
    if ($error->getCode() !== DOM_HIERARCHY_REQUEST_ERR) { throw $error; }
    echo "Второй корень order отклонён: отдельному заказу нужен свой документ\n";
}
echo $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
Второй корень order отклонён: отдельному заказу нужен свой документ
<catalog/>

try выполняет операцию, catch получает DOMException при нарушении дерева. getCode сообщает категорию; обрабатываем только ожидаемый запрет второго корня, остальные причины передаём выше через throw. Пустой order создался отдельно, но в документ не вставился.

Свойство этого классаЗначение и границы
$codeDOMException несёт числовой код категории ошибки. Сопоставляйте с DOM_*/Dom\* константой и контекстом операции; не показывайте сырые внутренние сообщения посетителю.

Интерфейсы: Throwable, Stringable.

Официальное описание DOMException; точные современные сигнатуры сверены с PHP 8.5.11.

DOMImplementation: назначение, свойства и операции

Implementation — объект фабрик для создания doctype/документов. Он полезен, когда документ надо создать сразу с корнем и DTD, либо получить HTML-скелет. Modern Implementation доступен у document.implementation; legacy можно new. Он не хранит каталог товаров и не является конфигурацией PHP-процесса.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMImplementation в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMImplementation представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Все методы этого класса: DOMImplementation::hasFeature, DOMImplementation::createDocumentType, DOMImplementation::createDocument.

Официальное описание DOMImplementation; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMImplementation::createDocument.

Создать документ контрактного namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$document = $implementation->createDocument("urn:catalog", "c:catalog");
echo $document->documentElement->namespaceURI, ":", $document->documentElement->localName, "
";
Ожидаемый результат
urn:catalog:catalog

URI не загружается по сети; это идентификатор пространства имён.

Dom\Implementation: назначение, свойства и операции

Implementation — объект фабрик для создания doctype/документов. Он полезен, когда документ надо создать сразу с корнем и DTD, либо получить HTML-скелет. Modern Implementation доступен у document.implementation; legacy можно new. Он не хранит каталог товаров и не является конфигурацией PHP-процесса.

Современное семейство Dom\, PHP8.4+.

Место Dom\Implementation в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Implementation представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Все методы этого класса: Dom\Implementation::createDocumentType, Dom\Implementation::createDocument, Dom\Implementation::createHTMLDocument.

Официальное описание Dom\Implementation; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Implementation::createHTMLDocument.

Получить заголовок и телоСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$document = $implementation->createHTMLDocument("Catalog");
echo $document->title, ":", $document->body->localName, "
";
Ожидаемый результат
Catalog:body

head/body созданы фабрикой автоматически.

DOMNode: назначение, свойства и операции

Общий тип узла помогает работать с деревом: Element содержит поля/детей, Text — символы, Document — всё дерево. Переменная с типом Node не гарантирует наличие атрибутов или тега. Сначала выясните nodeType/instanceof, затем выбирайте операцию. В modern Element.nodeValue=null; текст потомков читают через textContent. Присваивание textContent элементу удаляет всю вложенную разметку.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMNode в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMNode представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Node — общий тип. Чтобы выбрать действие, сначала различим Document, Element и Text на одном товаре. Свойства читаются стрелкой без скобок; firstChild даёт первого ребёнка.

Отличить целый документ, товар и его текстСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1">Desk</product>');

$product = $document->documentElement;
$text = $product->firstChild;
echo 'Документ: ', $document->nodeName, "\n";
echo 'Элемент: ', $product->nodeName, "\n";
echo 'Текст: ', $text->nodeName, ' = ', $text->nodeValue, "\n";
Ожидаемый результат
Документ: #document
Элемент: product
Текст: #text = Desk

Все три объекта являются узлами, но означают разные части дерева. nodeName у Text — служебное #text, а его nodeValue содержит Desk. У атрибута sku связь с товаром выражает ownerElement, не обычный parentNode. В этом файле перед Desk нет пробельного узла.

Свойство этого классаЗначение и границы
$nodeName: stringТолько чтение. Имя узла: у Element квалифицированное имя, у Text #text, у Document #document. Для XML-имени без префикса читайте localName.
$nodeValue: ?stringЗначение зависит от вида узла. Modern Element/Document возвращают null; CharacterData/Attr несут текст. Legacy Element имеет историческую текстовую семантику. Не используйте одно правило для всех узлов.
$nodeType: intТолько чтение. Числовой вид узла. Сравнивайте с XML_*_NODE или подходящей константой, не с произвольным магическим числом.
$parentNode: ?DOMNodeТолько чтение. Непосредственный родитель либо null у отсоединённого узла. У Attr обычного родителя нет: используйте ownerElement.
$parentElement: ?DOMElementТолько чтение. Родитель именно Element либо null; Document как родитель здесь не возвращается.
$childNodes: DOMNodeListТолько чтение. Живой список всех непосредственных детей, включая пробельные Text и Comment. Для элементов children/firstElementChild удобнее.
$firstChild: ?DOMNodeТолько чтение. Первый непосредственный узел либо null; это может быть пробельный Text, а не ожидаемый товар.
$lastChild: ?DOMNodeТолько чтение. Последний непосредственный узел любого типа либо null.
$previousSibling: ?DOMNodeТолько чтение. Предыдущий сосед того же родителя; может быть текстом/комментарием, null у первого.
$nextSibling: ?DOMNodeТолько чтение. Следующий сосед любого типа либо null у последнего.
$attributes: ?DOMNamedNodeMapТолько чтение. Карта атрибутов; в legacy Node без атрибутов может null. В modern Element это NamedNodeMap, включая xmlns-атрибуты.
$isConnected: boolТолько чтение. true, когда узел связан с Document через дерево. Наличие ownerDocument само по себе недостаточно: созданный, но не вставленный узел не подключён.
$ownerDocument: ?DOMDocumentТолько чтение. Документ-владелец native узла, либо null для Document/некоторых свободных legacy узлов. Владелец и родитель — разные связи.
$namespaceURI: ?stringТолько чтение. URI пространства имён либо null. Это идентификатор смысла, не инструкция загрузить страницу по сети.
$prefix: stringКороткий префикс имени. Modern null при отсутствии, legacy может пустая строка. В modern для изменения namespace/имени используйте rename; не присваивайте readonly prefix.
$localName: ?stringТолько чтение. Локальная часть имени без префикса; у узлов без XML-имени может null в legacy.
$baseURI: ?stringТолько чтение. Базовый URI для относительных ссылок, с учётом контекста/xml:base. Не является доказательством доверенности пути.
$textContent: stringСодержимое текстовых потомков; присваивание элементу заменяет его детей одним текстом. Разметка не сохраняется. Modern у Document/DocumentType null; nodeValue и textContent не взаимозаменяемы.

Все методы этого класса: DOMNode::appendChild, DOMNode::C14N, DOMNode::C14NFile, DOMNode::cloneNode, DOMNode::getLineNo, DOMNode::getNodePath, DOMNode::hasAttributes, DOMNode::hasChildNodes, DOMNode::insertBefore, DOMNode::isDefaultNamespace, DOMNode::isSameNode, DOMNode::isEqualNode, DOMNode::isSupported, DOMNode::lookupNamespaceURI, DOMNode::lookupPrefix, DOMNode::normalize, DOMNode::removeChild, DOMNode::replaceChild, DOMNode::contains, DOMNode::getRootNode, DOMNode::compareDocumentPosition, DOMNode::__sleep, DOMNode::__wakeup.

Официальное описание DOMNode; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\Node: назначение, свойства и операции

Общий тип узла помогает работать с деревом: Element содержит поля/детей, Text — символы, Document — всё дерево. Переменная с типом Node не гарантирует наличие атрибутов или тега. Сначала выясните nodeType/instanceof, затем выбирайте операцию. В modern Element.nodeValue=null; текст потомков читают через textContent. Присваивание textContent элементу удаляет всю вложенную разметку.

Современное семейство Dom\, PHP8.4+.

Место Dom\Node в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Node представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Node — общий тип. Чтобы выбрать действие, сначала различим Document, Element и Text на одном товаре. Свойства читаются стрелкой без скобок; firstChild даёт первого ребёнка.

Отличить целый документ, товар и его текстСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1">Desk</product>');

$product = $document->documentElement;
$text = $product->firstChild;
echo 'Документ: ', $document->nodeName, "\n";
echo 'Элемент: ', $product->nodeName, "\n";
echo 'Текст: ', $text->nodeName, ' = ', $text->nodeValue, "\n";
Ожидаемый результат
Документ: #document
Элемент: product
Текст: #text = Desk

Все три объекта являются узлами, но означают разные части дерева. nodeName у Text — служебное #text, а его nodeValue содержит Desk. У атрибута sku связь с товаром выражает ownerElement, не обычный parentNode. В этом файле перед Desk нет пробельного узла.

Свойство этого классаЗначение и границы
$nodeType: intТолько чтение. Числовой вид узла. Сравнивайте с XML_*_NODE или подходящей константой, не с произвольным магическим числом.
$nodeName: stringТолько чтение. Имя узла: у Element квалифицированное имя, у Text #text, у Document #document. Для XML-имени без префикса читайте localName.
$baseURI: stringТолько чтение. Базовый URI для относительных ссылок, с учётом контекста/xml:base. Не является доказательством доверенности пути.
$isConnected: boolТолько чтение. true, когда узел связан с Document через дерево. Наличие ownerDocument само по себе недостаточно: созданный, но не вставленный узел не подключён.
$ownerDocument: ?Dom\DocumentТолько чтение. Документ-владелец native узла, либо null для Document/некоторых свободных legacy узлов. Владелец и родитель — разные связи.
$parentNode: ?Dom\NodeТолько чтение. Непосредственный родитель либо null у отсоединённого узла. У Attr обычного родителя нет: используйте ownerElement.
$parentElement: ?Dom\ElementТолько чтение. Родитель именно Element либо null; Document как родитель здесь не возвращается.
$childNodes: Dom\NodeListТолько чтение. Живой список всех непосредственных детей, включая пробельные Text и Comment. Для элементов children/firstElementChild удобнее.
$firstChild: ?Dom\NodeТолько чтение. Первый непосредственный узел либо null; это может быть пробельный Text, а не ожидаемый товар.
$lastChild: ?Dom\NodeТолько чтение. Последний непосредственный узел любого типа либо null.
$previousSibling: ?Dom\NodeТолько чтение. Предыдущий сосед того же родителя; может быть текстом/комментарием, null у первого.
$nextSibling: ?Dom\NodeТолько чтение. Следующий сосед любого типа либо null у последнего.
$nodeValue: ?stringЗначение зависит от вида узла. Modern Element/Document возвращают null; CharacterData/Attr несут текст. Legacy Element имеет историческую текстовую семантику. Не используйте одно правило для всех узлов.
$textContent: ?stringСодержимое текстовых потомков; присваивание элементу заменяет его детей одним текстом. Разметка не сохраняется. Modern у Document/DocumentType null; nodeValue и textContent не взаимозаменяемы.

Все методы этого класса: Dom\Node::__construct, Dom\Node::getRootNode, Dom\Node::hasChildNodes, Dom\Node::normalize, Dom\Node::cloneNode, Dom\Node::isEqualNode, Dom\Node::isSameNode, Dom\Node::compareDocumentPosition, Dom\Node::contains, Dom\Node::lookupPrefix, Dom\Node::lookupNamespaceURI, Dom\Node::isDefaultNamespace, Dom\Node::insertBefore, Dom\Node::appendChild, Dom\Node::replaceChild, Dom\Node::removeChild, Dom\Node::getLineNo, Dom\Node::getNodePath, Dom\Node::C14N, Dom\Node::C14NFile, Dom\Node::__sleep, Dom\Node::__wakeup.

Официальное описание Dom\Node; точные современные сигнатуры сверены с PHP 8.5.11.

DOMNameSpaceNode: назначение, свойства и операции

Legacy DOMNameSpaceNode — специальная обёртка namespace-объявления, например xmlns:p="urn:price". Она не наследует DOMNode как обычный Attr, поэтому её нельзя передавать любой операции изменения дерева. Все её сведения читаются из связанного контекста; PHP-сериализация запрещена.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMNameSpaceNode в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMNameSpaceNode представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMNameSpaceNode::__sleep.

Обработать неподдерживаемый magic methodСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price"/>');
$node = $document->documentElement->getAttributeNode("xmlns:p");
try {
    $node->__sleep();
} catch (Exception $error) {
    echo "DOM serialization unsupported
";
}
Ожидаемый результат
DOM serialization unsupported

Не пытайтесь вызывать magic methods как способ сохранить native DOM.

Свойство этого классаЗначение и границы
$nodeName: stringТолько чтение. Имя узла: у Element квалифицированное имя, у Text #text, у Document #document. Для XML-имени без префикса читайте localName.
$nodeValue: ?stringТолько чтение. Значение зависит от вида узла. Modern Element/Document возвращают null; CharacterData/Attr несут текст. Legacy Element имеет историческую текстовую семантику. Не используйте одно правило для всех узлов.
$nodeType: intТолько чтение. Числовой вид узла. Сравнивайте с XML_*_NODE или подходящей константой, не с произвольным магическим числом.
$prefix: stringТолько чтение. Короткий префикс имени. Modern null при отсутствии, legacy может пустая строка. В modern для изменения namespace/имени используйте rename; не присваивайте readonly prefix.
$localName: ?stringТолько чтение. Локальная часть имени без префикса; у узлов без XML-имени может null в legacy.
$namespaceURI: ?stringТолько чтение. URI пространства имён либо null. Это идентификатор смысла, не инструкция загрузить страницу по сети.
$isConnected: boolТолько чтение. true, когда узел связан с Document через дерево. Наличие ownerDocument само по себе недостаточно: созданный, но не вставленный узел не подключён.
$ownerDocument: ?DOMDocumentТолько чтение. Документ-владелец native узла, либо null для Document/некоторых свободных legacy узлов. Владелец и родитель — разные связи.
$parentNode: ?DOMNodeТолько чтение. Непосредственный родитель либо null у отсоединённого узла. У Attr обычного родителя нет: используйте ownerElement.
$parentElement: ?DOMElementТолько чтение. Родитель именно Element либо null; Document как родитель здесь не возвращается.

Все методы этого класса: DOMNameSpaceNode::__sleep, DOMNameSpaceNode::__wakeup.

Официальное описание DOMNameSpaceNode; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\NamespaceInfo: назначение, свойства и операции

NamespaceInfo — современная readonly-запись о prefix, namespaceURI и element. Её создают getInScopeNamespaces/getDescendantNamespaces. Это снимок сведений о контексте, не XML-атрибут для вставки и не объект, который создают new. Для изменения XML используйте namespace-aware методы элемента.

Современное семейство Dom\, PHP8.4+.

Место Dom\NamespaceInfo в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\NamespaceInfo представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Element::getInScopeNamespaces.

Найти префикс доставки в контексте товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns:p="urn:price"><product xmlns:d="urn:delivery"/></catalog>');
$product = $document->documentElement->firstElementChild;
foreach ($product->getInScopeNamespaces() as $namespace) {
    if ($namespace->prefix === "d") {
        echo $namespace->namespaceURI, "
";
    }
}
Ожидаемый результат
urn:delivery

Запись говорит о namespace-контексте, а не о сетевом URL, который надо загрузить.

Свойство этого классаЗначение и границы
$prefix: ?stringТолько чтение. Короткий префикс имени. Modern null при отсутствии, legacy может пустая строка. В modern для изменения namespace/имени используйте rename; не присваивайте readonly prefix.
$namespaceURI: ?stringТолько чтение. URI пространства имён либо null. Это идентификатор смысла, не инструкция загрузить страницу по сети.
$element: Dom\ElementТолько чтение. У NamespaceInfo readonly элемент, для которого вычислен контекст namespace; не обязательно место исходного xmlns.

Все методы этого класса: Dom\NamespaceInfo::__construct.

Официальное описание Dom\NamespaceInfo; точные современные сигнатуры сверены с PHP 8.5.11.

DOMDocumentFragment: назначение, свойства и операции

Fragment — временный лоток узлов без собственного тега-обёртки. После вставки в родителя его дети перемещаются и лоток пустеет. Создавайте через документ, особенно в legacy: свободный new fragment может не иметь нужного владельца для изменения. Фрагмент не является валидным самостоятельным XML-документом с единственным корнем.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMDocumentFragment в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMDocumentFragment представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMDocumentFragment::appendXML.

Вставить две позиции и показать каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$fragment = $document->createDocumentFragment();
if (!$fragment->appendXML('<product>Tea</product><product>Coffee</product>')) {
    throw new RuntimeException('Некорректные позиции');
}
$document->documentElement->appendChild($fragment);
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><product>Tea</product><product>Coffee</product></catalog>

Fragment разрешает подготовить соседние элементы без дополнительной обёртки. После вставки в catalog переместились оба ребёнка; самого тега fragment на выходе нет.

Свойство этого классаЗначение и границы
$firstElementChild: ?DOMElementТолько чтение. Первый непосредственный Element либо null, пропускает Text и Comment.
$lastElementChild: ?DOMElementТолько чтение. Последний непосредственный Element либо null.
$childElementCount: intТолько чтение. Количество прямых дочерних элементов; не всех потомков и не всех childNodes.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMDocumentFragment::__construct, DOMDocumentFragment::appendXML, DOMDocumentFragment::append, DOMDocumentFragment::prepend, DOMDocumentFragment::replaceChildren.

Интерфейсы: DOMParentNode.

Официальное описание DOMDocumentFragment; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\DocumentFragment: назначение, свойства и операции

Fragment — временный лоток узлов без собственного тега-обёртки. После вставки в родителя его дети перемещаются и лоток пустеет. Создавайте через документ, особенно в legacy: свободный new fragment может не иметь нужного владельца для изменения. Фрагмент не является валидным самостоятельным XML-документом с единственным корнем.

Современное семейство Dom\, PHP8.4+.

Место Dom\DocumentFragment в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\DocumentFragment представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\DocumentFragment::appendXml.

Вставить две позиции и показать каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$fragment = $document->createDocumentFragment();
if (!$fragment->appendXml('<product>Tea</product><product>Coffee</product>')) {
    throw new RuntimeException('Некорректные позиции');
}
$document->documentElement->appendChild($fragment);
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><product>Tea</product><product>Coffee</product></catalog>

Fragment разрешает подготовить соседние элементы без дополнительной обёртки. После вставки в catalog переместились оба ребёнка; самого тега fragment на выходе нет.

Свойство этого классаЗначение и границы
$children: Dom\HTMLCollectionТолько чтение. Живая HTMLCollection прямых дочерних элементов (PHP8.5). Отдельно от childNodes, куда входят текст/комментарии.
$firstElementChild: ?Dom\ElementТолько чтение. Первый непосредственный Element либо null, пропускает Text и Comment.
$lastElementChild: ?Dom\ElementТолько чтение. Последний непосредственный Element либо null.
$childElementCount: intТолько чтение. Количество прямых дочерних элементов; не всех потомков и не всех childNodes.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\DocumentFragment::appendXml, Dom\DocumentFragment::append, Dom\DocumentFragment::prepend, Dom\DocumentFragment::replaceChildren, Dom\DocumentFragment::querySelector, Dom\DocumentFragment::querySelectorAll.

Интерфейсы: Dom\ParentNode.

Официальное описание Dom\DocumentFragment; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\Document: назначение, свойства и операции

Документ — владелец дерева и его фабрика, а documentElement — корень внутри него. Созданный createElement узел уже знает ownerDocument, но ещё не знает родителя. Это объясняет, почему создание, вставка, импорт и сериализация являются разными действиями. Современный Dom\Document — абстрактная база: создавайте XMLDocument/HTMLDocument статической фабрикой.

Современное семейство Dom\, PHP8.4+.

Место Dom\Document в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Document представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\XMLDocument::createEmpty.

Создать корень будущего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createEmpty();
$document->appendChild($document->createElement("catalog"));
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog/>

createEmpty создаёт владельца дерева, createElement — корень, appendChild соединяет их. Только после вставки корень попадает в сериализацию. Кодировка фабрики по умолчанию UTF-8.

Свойство этого классаЗначение и границы
$children: Dom\HTMLCollectionТолько чтение. Живая HTMLCollection прямых дочерних элементов (PHP8.5). Отдельно от childNodes, куда входят текст/комментарии.
$firstElementChild: ?Dom\ElementТолько чтение. Первый непосредственный Element либо null, пропускает Text и Comment.
$lastElementChild: ?Dom\ElementТолько чтение. Последний непосредственный Element либо null.
$childElementCount: intТолько чтение. Количество прямых дочерних элементов; не всех потомков и не всех childNodes.
$implementation: Dom\ImplementationТолько чтение. Объект фабрик DOMImplementation/Dom\Implementation для создания DocumentType и документов.
$URL: stringModern отражение URI документа. Это метаданные и база разрешения ссылок, не сетевой HTTP-клиент.
$documentURI: stringМестоположение/база документа. Изменение влияет на разрешение относительных ссылок; не помещайте сюда непроверенный клиентский URL.
$characterSet: stringModern каноническое имя кодировки документа. Входной overrideEncoding и кодировка сериализации имеют разные роли.
$charset: stringModern синоним characterSet для совместимого DOM-интерфейса.
$inputEncoding: stringModern синоним информации о кодировке документа; не определяет кодировку произвольной внешней PHP-строки.
$doctype: ?Dom\DocumentTypeТолько чтение. Объявление типа документа либо null. DTD и корень документа — разные узлы.
$documentElement: ?Dom\ElementТолько чтение. Корневой элемент документа либо null у пустого. Это не сам Document.
$body: ?Dom\HTMLElementHTML body либо frameset, null в неподходящем документе. Запись требует корректного HTMLElement нужного вида; это замена тела, не строковый HTML.
$head: ?Dom\HTMLElementТолько чтение. Первый подходящий HTML head либо null, только чтение.
$title: stringТекст заголовка HTML/SVG-документа по DOM-правилам. Не равен всему head и не исполняет HTML.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\Document::getElementsByTagName, Dom\Document::getElementsByTagNameNS, Dom\Document::getElementsByClassName, Dom\Document::createElement, Dom\Document::createElementNS, Dom\Document::createDocumentFragment, Dom\Document::createTextNode, Dom\Document::createCDATASection, Dom\Document::createComment, Dom\Document::createProcessingInstruction, Dom\Document::importNode, Dom\Document::adoptNode, Dom\Document::createAttribute, Dom\Document::createAttributeNS, Dom\Document::getElementById, Dom\Document::registerNodeClass, Dom\Document::schemaValidate, Dom\Document::schemaValidateSource, Dom\Document::relaxNgValidate, Dom\Document::relaxNgValidateSource, Dom\Document::append, Dom\Document::prepend, Dom\Document::replaceChildren, Dom\Document::importLegacyNode, Dom\Document::querySelector, Dom\Document::querySelectorAll.

Интерфейсы: Dom\ParentNode.

Официальное описание Dom\Document; точные современные сигнатуры сверены с PHP 8.5.11.

DOMDocument: назначение, свойства и операции

Документ — владелец дерева и его фабрика, а documentElement — корень внутри него. Созданный createElement узел уже знает ownerDocument, но ещё не знает родителя. Это объясняет, почему создание, вставка, импорт и сериализация являются разными действиями. Современный Dom\Document — абстрактная база: создавайте XMLDocument/HTMLDocument статической фабрикой.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMDocument в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMDocument представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMDocument::__construct.

Создать корневой элементСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument("1.0", "UTF-8");
$document->appendChild($document->createElement("catalog"));
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog/>

new создаёт документ, appendChild добавляет корень.

Свойство этого классаЗначение и границы
$doctype: ?DOMDocumentTypeТолько чтение. Объявление типа документа либо null. DTD и корень документа — разные узлы.
$implementation: DOMImplementationТолько чтение. Объект фабрик DOMImplementation/Dom\Implementation для создания DocumentType и документов.
$documentElement: ?DOMElementТолько чтение. Корневой элемент документа либо null у пустого. Это не сам Document.
$actualEncoding: ?stringТолько чтение. Историческое readonly отражение encoding у DOMDocument, deprecated PHP8.4. У DOMEntity всегда null и также deprecated; заменяйте подходящим актуальным свойством документа.
$encoding: ?stringКодировка сериализации legacy DOMDocument, изменяемая строка. Не заставляет loadHTML автоматически угадать кодировку входа. DOMEntity.encoding всегда null и deprecated PHP8.4.
$xmlEncoding: ?stringТолько чтение. Кодировка XML-документа. Современное свойство только читается; выбирайте её фабрикой. Legacy отражает encoding и может null, если не задана.
$standalone: boolСтарое имя флага XML standalone; используйте xmlStandalone. Это декларация XML, а не разрешение/запрет сетевого доступа.
$xmlStandalone: boolФлаг standalone XML-декларации. true/false меняет вывод декларации, но не является XXE-защитой.
$version: ?stringИсторическое имя XML-версии документа; используйте xmlVersion. DOMEntity.version всегда null, deprecated PHP8.4.
$xmlVersion: ?stringВерсия формата XML, обычно 1.0; не версия PHP. Произвольная строка не означает поддержку новой спецификации парсером.
$strictErrorChecking: boolLegacy: по умолчанию true, структурные ошибки вызывают DOMException. false может дать предупреждение/false; дерево от этого не становится корректным.
$documentURI: ?stringМестоположение/база документа. Изменение влияет на разрешение относительных ссылок; не помещайте сюда непроверенный клиентский URL.
$config: mixedТолько чтение. Исторический незавершённый интерфейс DOMConfiguration, deprecated PHP8.4. Не используйте как объект работающих настроек normalizeDocument.
$formatOutput: boolОтступы при сериализации XML. В legacy для загруженного дерева обычно нужно preserveWhiteSpace=false ДО load; сохранённые пробельные Text мешают ожидаемому форматированию.
$validateOnParse: boolLegacy: загрузка и проверка DTD при разборе (по умолчанию false). Может затрагивать внешние ресурсы; не включайте для случайного XML клиента.
$resolveExternals: boolLegacy: разрешение внешних сущностей/DTD. Не включайте для недоверенного XML; путь и сеть должны контролироваться отдельно.
$preserveWhiteSpace: boolLegacy true по умолчанию сохраняет пробельные Text. false до загрузки соответствует LIBXML_NOBLANKS, но может менять смысл смешанного текста.
$recover: boolLegacy попытка восстановить некорректный XML, специфичная libxml. Полученное дерево не доказывает, что вход полностью и правильно принят.
$substituteEntities: boolLegacy подстановка сущностей, false по умолчанию. Меняет структуру/текст и может открыть XXE при разрешённых внешних ресурсах.
$firstElementChild: ?DOMElementТолько чтение. Первый непосредственный Element либо null, пропускает Text и Comment.
$lastElementChild: ?DOMElementТолько чтение. Последний непосредственный Element либо null.
$childElementCount: intТолько чтение. Количество прямых дочерних элементов; не всех потомков и не всех childNodes.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMDocument::__construct, DOMDocument::createAttribute, DOMDocument::createAttributeNS, DOMDocument::createCDATASection, DOMDocument::createComment, DOMDocument::createDocumentFragment, DOMDocument::createElement, DOMDocument::createElementNS, DOMDocument::createEntityReference, DOMDocument::createProcessingInstruction, DOMDocument::createTextNode, DOMDocument::getElementById, DOMDocument::getElementsByTagName, DOMDocument::getElementsByTagNameNS, DOMDocument::importNode, DOMDocument::load, DOMDocument::loadXML, DOMDocument::normalizeDocument, DOMDocument::registerNodeClass, DOMDocument::save, DOMDocument::loadHTML, DOMDocument::loadHTMLFile, DOMDocument::saveHTML, DOMDocument::saveHTMLFile, DOMDocument::saveXML, DOMDocument::schemaValidate, DOMDocument::schemaValidateSource, DOMDocument::relaxNGValidate, DOMDocument::relaxNGValidateSource, DOMDocument::validate, DOMDocument::xinclude, DOMDocument::adoptNode, DOMDocument::append, DOMDocument::prepend, DOMDocument::replaceChildren.

Интерфейсы: DOMParentNode.

Официальное описание DOMDocument; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\HTMLDocument: назначение, свойства и операции

Современный HTMLDocument следует актуальным правилам HTML-разбора, включая восстановление дерева и HTML namespace. Он не исполняет JavaScript и не умеет получать DOM после выполнения SPA. CSS-поиск может быть проще XPath, но в XPath HTML-элементы требуют регистрации URI http://www.w3.org/1999/xhtml. Шаблон template имеет особую fragment-семантику; отключение HTML namespace меняет её.

Современное семейство Dom\, PHP8.4+.

Место Dom\HTMLDocument в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\HTMLDocument представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: Dom\Document. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\HTMLDocument::createEmpty, Dom\HTMLDocument::createFromFile, Dom\HTMLDocument::createFromString, Dom\HTMLDocument::saveXml, Dom\HTMLDocument::saveXmlFile, Dom\HTMLDocument::saveHtml, Dom\HTMLDocument::saveHtmlFile.

Интерфейсы: Dom\ParentNode.

Официальное описание Dom\HTMLDocument; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\HTMLDocument::createFromFile.

Прочитать товар из нового документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromFile("catalog.html");
echo $document->getElementsByTagName("p")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.

Dom\XMLDocument: назначение, свойства и операции

Современный XMLDocument разбирает строгий XML, хранит его целиком и сериализует обратно. Для новых XML-интеграций он даёт последовательные namespace-aware правила; legacy DOMDocument остаётся нужен для старых библиотек. XMLDocument final; для предметного поведения регистрируйте поддерживаемый класс узла или используйте отдельный сервис.

Современное семейство Dom\, PHP8.4+.

Место Dom\XMLDocument в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\XMLDocument представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\XMLDocument::createFromFile.

Прочитать товар из нового документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromFile("catalog.xml");
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.

Свойство этого классаЗначение и границы
$xmlEncoding: stringТолько чтение. Кодировка XML-документа. Современное свойство только читается; выбирайте её фабрикой. Legacy отражает encoding и может null, если не задана.
$xmlStandalone: boolФлаг standalone XML-декларации. true/false меняет вывод декларации, но не является XXE-защитой.
$xmlVersion: stringВерсия формата XML, обычно 1.0; не версия PHP. Произвольная строка не означает поддержку новой спецификации парсером.
$formatOutput: boolОтступы при сериализации XML. В legacy для загруженного дерева обычно нужно preserveWhiteSpace=false ДО load; сохранённые пробельные Text мешают ожидаемому форматированию.

Унаследованные свойства и операции: Dom\Document. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\XMLDocument::createEmpty, Dom\XMLDocument::createFromFile, Dom\XMLDocument::createFromString, Dom\XMLDocument::createEntityReference, Dom\XMLDocument::validate, Dom\XMLDocument::xinclude, Dom\XMLDocument::saveXml, Dom\XMLDocument::saveXmlFile.

Интерфейсы: Dom\ParentNode.

Официальное описание Dom\XMLDocument; точные современные сигнатуры сверены с PHP 8.5.11.

DOMAttr: назначение, свойства и операции

Атрибут хранит одно строковое поле элемента. У него ownerElement, а parentNode не является обычной связью ребёнка. Даже число stock="4" остаётся строкой до вашей проверки и преобразования. Legacy value подставляет сущности; modern value хранит буквальные данные. Атрибут без префикса не наследует default namespace элемента.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMAttr в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMAttr представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMElement::getAttributeNode.

Прочитать объект атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
echo $attribute->value, "
";
Ожидаемый результат
4

Стрелка value обращается к свойству Attr.

Свойство этого классаЗначение и границы
$name: stringТолько чтение. Квалифицированное имя Attr либо имя DocumentType; у enum name — PHP-имя case. Не значение атрибута.
$specified: boolТолько чтение. Историческое свойство Attr, всегда true; не доказывает, что атрибут физически пришёл из исходного файла, а не из DTD/default.
$value: stringИзменяемая строка, но legacy интерпретирует & как начало XML-сущности. Присваивание уже прочитанной строки с & может испортить её; для буквальных данных используйте Element::setAttribute.
$ownerElement: ?DOMElementТолько чтение. Элемент-владелец Attr либо null после отсоединения. Атрибут не обычный ребёнок элемента.
$schemaTypeInfo: mixedТолько чтение. Legacy-заглушка, всегда null; не даёт объект результата XSD-типизации. Для проверки используйте schemaValidate и свой контракт данных.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMAttr::__construct, DOMAttr::isId.

Официальное описание DOMAttr; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\Attr: назначение, свойства и операции

Атрибут хранит одно строковое поле элемента. У него ownerElement, а parentNode не является обычной связью ребёнка. Даже число stock="4" остаётся строкой до вашей проверки и преобразования. Legacy value подставляет сущности; modern value хранит буквальные данные. Атрибут без префикса не наследует default namespace элемента.

Современное семейство Dom\, PHP8.4+.

Место Dom\Attr в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Attr представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Element::getAttributeNode.

Прочитать объект атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
echo $attribute->value, "
";
Ожидаемый результат
4

Стрелка value обращается к свойству Attr.

Свойство этого классаЗначение и границы
$namespaceURI: ?stringТолько чтение. URI пространства имён либо null. Это идентификатор смысла, не инструкция загрузить страницу по сети.
$prefix: ?stringТолько чтение. Короткий префикс имени. Modern null при отсутствии, legacy может пустая строка. В modern для изменения namespace/имени используйте rename; не присваивайте readonly prefix.
$localName: stringТолько чтение. Локальная часть имени без префикса; у узлов без XML-имени может null в legacy.
$name: stringТолько чтение. Квалифицированное имя Attr либо имя DocumentType; у enum name — PHP-имя case. Не значение атрибута.
$value: stringИзменяемая строка без подстановки сущностей. В отличие от DOMAttr, & остаётся буквальными данными и экранируется сериализатором.
$ownerElement: ?Dom\ElementТолько чтение. Элемент-владелец Attr либо null после отсоединения. Атрибут не обычный ребёнок элемента.
$specified: boolТолько чтение. Историческое свойство Attr, всегда true; не доказывает, что атрибут физически пришёл из исходного файла, а не из DTD/default.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\Attr::isId, Dom\Attr::rename.

Официальное описание Dom\Attr; точные современные сигнатуры сверены с PHP 8.5.11.

DOMElement: назначение, свойства и операции

Элемент — конкретная сущность разметки: product, title, div. Атрибуты описывают поля, дочерние элементы задают вложенную структуру, Text хранит текст. Один и тот же объект может переместиться между родителями; две переменные могут ссылаться на него одновременно. Modern innerHTML/outerHTML разбирают разметку; textContent вставляет буквальный текст.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMElement в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMElement представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMElement::setAttribute.

Обновить остаток в XML товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->setAttribute("stock", "6");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="6"/>

setAttribute меняет выбранное поле. saveXml превращает изменённый узел в строку; другие атрибуты остаются.

Свойство этого классаЗначение и границы
$tagName: stringТолько чтение. Имя элемента; в modern HTML namespace HTML-документа может возвращаться в верхнем регистре. Для стабильного локального имени используйте localName.
$className: stringСтрока атрибута class. Присваивание заменяет весь набор CSS-меток; это не имя PHP-класса.
$id: stringСтрока атрибута id. Она не является серверным разрешением и не гарантирует отсутствие дублей в документе.
$schemaTypeInfo: mixedТолько чтение. Legacy-заглушка, всегда null; не даёт объект результата XSD-типизации. Для проверки используйте schemaValidate и свой контракт данных.
$firstElementChild: ?DOMElementТолько чтение. Первый непосредственный Element либо null, пропускает Text и Comment.
$lastElementChild: ?DOMElementТолько чтение. Последний непосредственный Element либо null.
$childElementCount: intТолько чтение. Количество прямых дочерних элементов; не всех потомков и не всех childNodes.
$previousElementSibling: ?DOMElementТолько чтение. Предыдущий сосед-Element, пропуская Text/Comment; null если нет.
$nextElementSibling: ?DOMElementТолько чтение. Следующий сосед-Element, пропуская Text/Comment; null если нет.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMElement::__construct, DOMElement::getAttribute, DOMElement::getAttributeNames, DOMElement::getAttributeNS, DOMElement::getAttributeNode, DOMElement::getAttributeNodeNS, DOMElement::getElementsByTagName, DOMElement::getElementsByTagNameNS, DOMElement::hasAttribute, DOMElement::hasAttributeNS, DOMElement::removeAttribute, DOMElement::removeAttributeNS, DOMElement::removeAttributeNode, DOMElement::setAttribute, DOMElement::setAttributeNS, DOMElement::setAttributeNode, DOMElement::setAttributeNodeNS, DOMElement::setIdAttribute, DOMElement::setIdAttributeNS, DOMElement::setIdAttributeNode, DOMElement::toggleAttribute, DOMElement::remove, DOMElement::before, DOMElement::after, DOMElement::replaceWith, DOMElement::append, DOMElement::prepend, DOMElement::replaceChildren, DOMElement::insertAdjacentElement, DOMElement::insertAdjacentText.

Интерфейсы: DOMParentNode, DOMChildNode.

Официальное описание DOMElement; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\Element: назначение, свойства и операции

Элемент — конкретная сущность разметки: product, title, div. Атрибуты описывают поля, дочерние элементы задают вложенную структуру, Text хранит текст. Один и тот же объект может переместиться между родителями; две переменные могут ссылаться на него одновременно. Modern innerHTML/outerHTML разбирают разметку; textContent вставляет буквальный текст.

Современное семейство Dom\, PHP8.4+.

Место Dom\Element в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Element представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Element::setAttribute.

Обновить остаток в XML товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->setAttribute("stock", "6");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="6"/>

setAttribute меняет выбранное поле. saveXml превращает изменённый узел в строку; другие атрибуты остаются.

Свойство этого классаЗначение и границы
$namespaceURI: ?stringТолько чтение. URI пространства имён либо null. Это идентификатор смысла, не инструкция загрузить страницу по сети.
$prefix: ?stringТолько чтение. Короткий префикс имени. Modern null при отсутствии, legacy может пустая строка. В modern для изменения namespace/имени используйте rename; не присваивайте readonly prefix.
$localName: stringТолько чтение. Локальная часть имени без префикса; у узлов без XML-имени может null в legacy.
$tagName: stringТолько чтение. Имя элемента; в modern HTML namespace HTML-документа может возвращаться в верхнем регистре. Для стабильного локального имени используйте localName.
$children: Dom\HTMLCollectionТолько чтение. Живая HTMLCollection прямых дочерних элементов (PHP8.5). Отдельно от childNodes, куда входят текст/комментарии.
$firstElementChild: ?Dom\ElementТолько чтение. Первый непосредственный Element либо null, пропускает Text и Comment.
$lastElementChild: ?Dom\ElementТолько чтение. Последний непосредственный Element либо null.
$childElementCount: intТолько чтение. Количество прямых дочерних элементов; не всех потомков и не всех childNodes.
$previousElementSibling: ?Dom\ElementТолько чтение. Предыдущий сосед-Element, пропуская Text/Comment; null если нет.
$nextElementSibling: ?Dom\ElementТолько чтение. Следующий сосед-Element, пропуская Text/Comment; null если нет.
$id: stringСтрока атрибута id. Она не является серверным разрешением и не гарантирует отсутствие дублей в документе.
$className: stringСтрока атрибута class. Присваивание заменяет весь набор CSS-меток; это не имя PHP-класса.
$classList: Dom\TokenListТолько чтение. Связанный TokenList атрибута class. Само свойство readonly, но методы add/remove/toggle меняют class у этого элемента.
$attributes: Dom\NamedNodeMapТолько чтение. Карта атрибутов; в legacy Node без атрибутов может null. В modern Element это NamedNodeMap, включая xmlns-атрибуты.
$innerHTML: stringModern: сериализованный HTML/XML внутри элемента; запись разбирает разметку в его контексте и заменяет детей. Для пользовательского текста textContent. Не HTML-санитайзер.
$outerHTML: stringModern PHP8.5: сериализация самого элемента с детьми; запись заменяет его в родителе разобранной разметкой. Старая PHP-ссылка остаётся на отсоединённый старый объект. Недопустимая структура/Document-родитель может вызвать DOMException.
$substitutedNodeValue: stringModern Element: текст с подстановкой XML-сущностей. Присваивание использует entity-aware libxml-семантику; для обычного буквального текста textContent/createTextNode предсказуемее.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\Element::hasAttributes, Dom\Element::getAttributeNames, Dom\Element::getAttribute, Dom\Element::getAttributeNS, Dom\Element::setAttribute, Dom\Element::setAttributeNS, Dom\Element::removeAttribute, Dom\Element::removeAttributeNS, Dom\Element::toggleAttribute, Dom\Element::hasAttribute, Dom\Element::hasAttributeNS, Dom\Element::getAttributeNode, Dom\Element::getAttributeNodeNS, Dom\Element::setAttributeNode, Dom\Element::setAttributeNodeNS, Dom\Element::removeAttributeNode, Dom\Element::getElementsByTagName, Dom\Element::getElementsByTagNameNS, Dom\Element::getElementsByClassName, Dom\Element::insertAdjacentElement, Dom\Element::insertAdjacentText, Dom\Element::insertAdjacentHTML, Dom\Element::setIdAttribute, Dom\Element::setIdAttributeNS, Dom\Element::setIdAttributeNode, Dom\Element::remove, Dom\Element::before, Dom\Element::after, Dom\Element::replaceWith, Dom\Element::append, Dom\Element::prepend, Dom\Element::replaceChildren, Dom\Element::querySelector, Dom\Element::querySelectorAll, Dom\Element::closest, Dom\Element::matches, Dom\Element::getInScopeNamespaces, Dom\Element::getDescendantNamespaces, Dom\Element::rename.

Интерфейсы: Dom\ParentNode, Dom\ChildNode.

Официальное описание Dom\Element; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\HTMLElement: назначение, свойства и операции

Это специализация modern Element для HTML namespace. Она не равна любому XML-элементу с похожим именем. Различие важно для регистра tagName, булевых HTML-атрибутов, сериализации void-элементов и template. При rename запрещено перескакивать из/в HTML namespace, поскольку он связан с классом объекта.

Современное семейство Dom\, PHP8.4+.

Место Dom\HTMLElement в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\HTMLElement представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: Dom\Element. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Интерфейсы: Dom\ChildNode, Dom\ParentNode.

Официальное описание Dom\HTMLElement; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Element::insertAdjacentHTML.

Вставить готовую метку шаблонаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$article = $document->createElement("article");
$article->insertAdjacentHTML(Dom\AdjacentPosition::BeforeEnd, "<strong>Sale</strong>");
echo $document->saveHtml($article), "
";
Ожидаемый результат
<article><strong>Sale</strong></article>

Шаблон принадлежит приложению, внешних пользовательских тегов здесь нет.

DOMComment: назначение, свойства и операции

Comment хранит заметку <!--...--> в дереве. Она сериализуется и доступна получателю: это плохое место для пароля, токена или скрытой от посетителя информации. Для XML избегайте -- и завершающего -. Комментарий не входит в обычный текст Element.textContent.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMComment в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMComment представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: DOMCharacterData. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMComment::__construct.

Интерфейсы: DOMChildNode.

Официальное описание DOMComment; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMDocument::createComment.

Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$comment = $document->createComment("Generated catalog");
$document->documentElement->appendChild($comment);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><!--Generated catalog--></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Dom\Comment: назначение, свойства и операции

Comment хранит заметку <!--...--> в дереве. Она сериализуется и доступна получателю: это плохое место для пароля, токена или скрытой от посетителя информации. Для XML избегайте -- и завершающего -. Комментарий не входит в обычный текст Element.textContent.

Современное семейство Dom\, PHP8.4+.

Место Dom\Comment в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Comment представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: Dom\CharacterData. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Интерфейсы: Dom\ChildNode.

Официальное описание Dom\Comment; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Document::createComment.

Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$comment = $document->createComment("Generated catalog");
$document->documentElement->appendChild($comment);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><!--Generated catalog--></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

DOMCdataSection: назначение, свойства и операции

CDATASection — разновидность Text с особой XML-сериализацией <![CDATA[...]]>. Это не HTML-санитайзер и не контейнер произвольных бинарных байтов. Внутри одной секции нельзя сохранить ]]> без разбиения/другого способа передачи. Modern HTMLDocument такую секцию не создаёт.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMCdataSection в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMCdataSection представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: DOMText. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMCdataSection::__construct.

Интерфейсы: DOMChildNode.

Официальное описание DOMCdataSection; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMDocument::createCDATASection.

Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createCDATASection("<b>Sale</b>");
$document->documentElement->appendChild($node);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><![CDATA[<b>Sale</b>]]></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Dom\CDATASection: назначение, свойства и операции

CDATASection — разновидность Text с особой XML-сериализацией <![CDATA[...]]>. Это не HTML-санитайзер и не контейнер произвольных бинарных байтов. Внутри одной секции нельзя сохранить ]]> без разбиения/другого способа передачи. Modern HTMLDocument такую секцию не создаёт.

Современное семейство Dom\, PHP8.4+.

Место Dom\CDATASection в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\CDATASection представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: Dom\Text. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Интерфейсы: Dom\ChildNode.

Официальное описание Dom\CDATASection; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Document::createCDATASection.

Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createCDATASection("<b>Sale</b>");
$document->documentElement->appendChild($node);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><![CDATA[<b>Sale</b>]]></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

DOMDocumentType: назначение, свойства и операции

DocumentType описывает <!DOCTYPE>, включая имя, внешние идентификаторы и внутреннюю DTD. Это не корневой Element, его нельзя положить внутрь product. Создание doctype не означает валидацию или загрузку внешней DTD. В современном API некоторые ChildNode-операции над DTD ограничены реализацией; для замены используйте документные операции, а не мутацию комментариями.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMDocumentType в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMDocumentType представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMImplementation::createDocumentType.

Создать тип без загрузки внешнего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$doctype = $implementation->createDocumentType("catalog", "", "catalog.dtd");
echo $doctype->name, ":", $doctype->systemId, "
";
Ожидаемый результат
catalog:catalog.dtd

В примере файл catalog.dtd не требуется: DTD не читается этим вызовом.

Свойство этого классаЗначение и границы
$name: stringТолько чтение. Квалифицированное имя Attr либо имя DocumentType; у enum name — PHP-имя case. Не значение атрибута.
$entities: DOMNamedNodeMapТолько чтение. Карта DTD-объявлений сущностей, не найденных Element. Modern DtdNamedNodeMap, legacy DOMNamedNodeMap.
$notations: DOMNamedNodeMapТолько чтение. Карта DTD-нотаций для обозначения формата непарсенных сущностей. Это метаданные, а не декодер файла.
$publicId: stringТолько чтение. Публичный идентификатор DTD/сущности/нотации; пустая строка или null зависят от класса и отсутствия объявления.
$systemId: stringТолько чтение. Системный идентификатор, часто путь/URI. Чтение свойства не разрешает загружать его; источник должен быть доверенным.
$internalSubset: ?stringТолько чтение. Строка внутренней части DTD либо null. Не является обычным текстом корневого элемента.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Официальное описание DOMDocumentType; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\DocumentType: назначение, свойства и операции

DocumentType описывает <!DOCTYPE>, включая имя, внешние идентификаторы и внутреннюю DTD. Это не корневой Element, его нельзя положить внутрь product. Создание doctype не означает валидацию или загрузку внешней DTD. В современном API некоторые ChildNode-операции над DTD ограничены реализацией; для замены используйте документные операции, а не мутацию комментариями.

Современное семейство Dom\, PHP8.4+.

Место Dom\DocumentType в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\DocumentType представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Implementation::createDocumentType.

Создать тип без загрузки внешнего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$doctype = $implementation->createDocumentType("catalog", "", "catalog.dtd");
echo $doctype->name, ":", $doctype->systemId, "
";
Ожидаемый результат
catalog:catalog.dtd

В примере файл catalog.dtd не требуется: DTD не читается этим вызовом.

Свойство этого классаЗначение и границы
$name: stringТолько чтение. Квалифицированное имя Attr либо имя DocumentType; у enum name — PHP-имя case. Не значение атрибута.
$entities: Dom\DtdNamedNodeMapТолько чтение. Карта DTD-объявлений сущностей, не найденных Element. Modern DtdNamedNodeMap, legacy DOMNamedNodeMap.
$notations: Dom\DtdNamedNodeMapТолько чтение. Карта DTD-нотаций для обозначения формата непарсенных сущностей. Это метаданные, а не декодер файла.
$publicId: stringТолько чтение. Публичный идентификатор DTD/сущности/нотации; пустая строка или null зависят от класса и отсутствия объявления.
$systemId: stringТолько чтение. Системный идентификатор, часто путь/URI. Чтение свойства не разрешает загружать его; источник должен быть доверенным.
$internalSubset: ?stringТолько чтение. Строка внутренней части DTD либо null. Не является обычным текстом корневого элемента.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: Dom\DocumentType::remove, Dom\DocumentType::before, Dom\DocumentType::after, Dom\DocumentType::replaceWith.

Интерфейсы: Dom\ChildNode.

Официальное описание Dom\DocumentType; точные современные сигнатуры сверены с PHP 8.5.11.

DOMNotation: назначение, свойства и операции

Notation — DTD-описание вида внешних непарсенных данных. Оно связывает имя с publicId/systemId; PHP DOM не декодирует по нему изображения/видео автоматически. Получайте объект из doctype.notations. В обычном каталоге без DTD этот механизм не требуется.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMNotation в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMNotation представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Notation нужна старым XML-контрактам с не-XML вложениями. Сначала сущность cover сообщает имя нотации jpeg, затем карта notations даёт заявленный формат. Это метаданные, не загрузчик картинки.

Узнать формат вложения из деклараций DTDСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE item [<!NOTATION jpeg SYSTEM "image/jpeg"><!ENTITY cover SYSTEM "cover.jpg" NDATA jpeg>]><item/>');

$doctype = $document->doctype;
$cover = $doctype->entities->getNamedItem('cover');
$format = $doctype->notations->getNamedItem($cover->notationName);
echo 'Вложение: ', $cover->systemId, "\n";
echo 'Заявленный формат: ', $format->systemId, "\n";
Ожидаемый результат
Вложение: cover.jpg
Заявленный формат: image/jpeg

NDATA связывает имя вложения cover с нотацией jpeg. Здесь прочитаны только заявления автора XML: наличие cover.jpg и настоящий MIME его байтов не проверялись. Этот файл не нужен для запуска примера и не открывается.

Свойство этого классаЗначение и границы
$publicId: stringТолько чтение. Публичный идентификатор DTD/сущности/нотации; пустая строка или null зависят от класса и отсутствия объявления.
$systemId: stringТолько чтение. Системный идентификатор, часто путь/URI. Чтение свойства не разрешает загружать его; источник должен быть доверенным.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Официальное описание DOMNotation; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\Notation: назначение, свойства и операции

Notation — DTD-описание вида внешних непарсенных данных. Оно связывает имя с publicId/systemId; PHP DOM не декодирует по нему изображения/видео автоматически. Получайте объект из doctype.notations. В обычном каталоге без DTD этот механизм не требуется.

Современное семейство Dom\, PHP8.4+.

Место Dom\Notation в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Notation представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Notation нужна старым XML-контрактам с не-XML вложениями. Сначала сущность cover сообщает имя нотации jpeg, затем карта notations даёт заявленный формат. Это метаданные, не загрузчик картинки. В проверенной Windows-сборке PHP8.5.11 чтение systemId у Dom\Notation аварийно завершило процесс. Поэтому рабочий пример этой страницы извлекает метаданные через совместимый DOMDocument; это ограничение проверенной реализации, а не обещание одинакового поведения всех версий. Современный контракт ниже сохранён; try/catch не способен исправить нативное падение процесса.

Узнать формат вложения из деклараций DTD через совместимый DOMСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE item [<!NOTATION jpeg SYSTEM "image/jpeg"><!ENTITY cover SYSTEM "cover.jpg" NDATA jpeg>]><item/>');

$doctype = $document->doctype;
$cover = $doctype->entities->getNamedItem('cover');
$format = $doctype->notations->getNamedItem($cover->notationName);
echo 'Вложение: ', $cover->systemId, "\n";
echo 'Заявленный формат: ', $format->systemId, "\n";
Ожидаемый результат
Вложение: cover.jpg
Заявленный формат: image/jpeg

NDATA связывает имя вложения cover с нотацией jpeg. Здесь прочитаны только заявления автора XML: наличие cover.jpg и настоящий MIME его байтов не проверялись. Этот файл не нужен для запуска примера и не открывается.

Свойство этого классаЗначение и границы
$publicId: stringТолько чтение. Публичный идентификатор DTD/сущности/нотации; пустая строка или null зависят от класса и отсутствия объявления.
$systemId: stringТолько чтение. Системный идентификатор, часто путь/URI. Чтение свойства не разрешает загружать его; источник должен быть доверенным.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Официальное описание Dom\Notation; точные современные сигнатуры сверены с PHP 8.5.11.

DOMEntity: назначение, свойства и операции

Entity — объявление сущности из DTD. Parsed entity содержит подставляемое содержимое; unparsed entity ссылается на внешний объект и notation. Это не обычный узел товара, который создают и редактируют как Element. Поля устаревшей кодировки legacy Entity всегда null и deprecated с PHP8.4; не используйте их для определения кодировки документа.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMEntity в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMEntity представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Entity — объявленная в DTD сущность. Это может быть текстовая замена или ссылка на не-XML вложение. Здесь каталог вложений сообщает имя и адрес картинки; EntityReference в содержимом XML — другой вид узла. Не считайте Entity.textContent универсальным способом прочитать объявленную замену: в проверенном DOM оно может быть пустым.

Прочитать адрес объявленного вложенияСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE item [<!NOTATION jpeg SYSTEM "image/jpeg"><!ENTITY cover SYSTEM "cover.jpg" NDATA jpeg>]><item/>');

$entity = $document->doctype->entities->getNamedItem('cover');
echo 'Объявлено имя: ', $entity->nodeName, "\n";
echo 'Адрес вложения: ', $entity->systemId, "\n";
echo 'Нотация: ', $entity->notationName, "\n";
Ожидаемый результат
Объявлено имя: cover
Адрес вложения: cover.jpg
Нотация: jpeg

DocumentType хранит DTD, entities — именованную карту её объявлений, getNamedItem выбирает cover. SYSTEM задаёт идентификатор, здесь относительный адрес cover.jpg; NDATA указывает, что это не XML, и называет нотацию jpeg. Байты картинки не открываются: в примере нужны именно метаданные объявления.

Свойство этого классаЗначение и границы
$publicId: ?stringТолько чтение. Публичный идентификатор DTD/сущности/нотации; пустая строка или null зависят от класса и отсутствия объявления.
$systemId: ?stringТолько чтение. Системный идентификатор, часто путь/URI. Чтение свойства не разрешает загружать его; источник должен быть доверенным.
$notationName: ?stringТолько чтение. У непарсенной Entity имя связанной нотации; у парсенной сущности null.
$actualEncoding: ?stringТолько чтение. Историческое readonly отражение encoding у DOMDocument, deprecated PHP8.4. У DOMEntity всегда null и также deprecated; заменяйте подходящим актуальным свойством документа.
$encoding: ?stringТолько чтение. Кодировка сериализации legacy DOMDocument, изменяемая строка. Не заставляет loadHTML автоматически угадать кодировку входа. DOMEntity.encoding всегда null и deprecated PHP8.4.
$version: ?stringТолько чтение. Историческое имя XML-версии документа; используйте xmlVersion. DOMEntity.version всегда null, deprecated PHP8.4.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Официальное описание DOMEntity; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\Entity: назначение, свойства и операции

Entity — объявление сущности из DTD. Parsed entity содержит подставляемое содержимое; unparsed entity ссылается на внешний объект и notation. Это не обычный узел товара, который создают и редактируют как Element. Поля устаревшей кодировки legacy Entity всегда null и deprecated с PHP8.4; не используйте их для определения кодировки документа.

Современное семейство Dom\, PHP8.4+.

Место Dom\Entity в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\Entity представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Entity — объявленная в DTD сущность. Это может быть текстовая замена или ссылка на не-XML вложение. Здесь каталог вложений сообщает имя и адрес картинки; EntityReference в содержимом XML — другой вид узла. Не считайте Entity.textContent универсальным способом прочитать объявленную замену: в проверенном DOM оно может быть пустым.

Прочитать адрес объявленного вложенияСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE item [<!NOTATION jpeg SYSTEM "image/jpeg"><!ENTITY cover SYSTEM "cover.jpg" NDATA jpeg>]><item/>');

$entity = $document->doctype->entities->getNamedItem('cover');
echo 'Объявлено имя: ', $entity->nodeName, "\n";
echo 'Адрес вложения: ', $entity->systemId, "\n";
echo 'Нотация: ', $entity->notationName, "\n";
Ожидаемый результат
Объявлено имя: cover
Адрес вложения: cover.jpg
Нотация: jpeg

DocumentType хранит DTD, entities — именованную карту её объявлений, getNamedItem выбирает cover. SYSTEM задаёт идентификатор, здесь относительный адрес cover.jpg; NDATA указывает, что это не XML, и называет нотацию jpeg. Байты картинки не открываются: в примере нужны именно метаданные объявления.

Свойство этого классаЗначение и границы
$publicId: ?stringТолько чтение. Публичный идентификатор DTD/сущности/нотации; пустая строка или null зависят от класса и отсутствия объявления.
$systemId: ?stringТолько чтение. Системный идентификатор, часто путь/URI. Чтение свойства не разрешает загружать его; источник должен быть доверенным.
$notationName: ?stringТолько чтение. У непарсенной Entity имя связанной нотации; у парсенной сущности null.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Официальное описание Dom\Entity; точные современные сигнатуры сверены с PHP 8.5.11.

DOMEntityReference: назначение, свойства и операции

EntityReference — место обращения &name; к объявленной сущности, а Entity — само объявление. В modern DOM такие узлы могут появляться при XML-разборе, но стандартные методы вставки не принимают вручную созданную EntityReference. Для буквального текста выбирайте Text; для чтения содержимого внимательно учитывайте политику подстановки.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMEntityReference в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMEntityReference представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMEntityReference::__construct.

Официальное описание DOMEntityReference; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMDocument::createEntityReference.

Создать ссылку на внутреннюю сущностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE catalog [<!ENTITY tea "Tea">]><catalog/>');
$entity = $document->createEntityReference("tea");
$document->documentElement->appendChild($entity);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>&tea;</catalog>

DTD принадлежит приложению; внешняя подгрузка не используется.

Dom\EntityReference: назначение, свойства и операции

EntityReference — место обращения &name; к объявленной сущности, а Entity — само объявление. В modern DOM такие узлы могут появляться при XML-разборе, но стандартные методы вставки не принимают вручную созданную EntityReference. Для буквального текста выбирайте Text; для чтения содержимого внимательно учитывайте политику подстановки.

Современное семейство Dom\, PHP8.4+.

Место Dom\EntityReference в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\EntityReference представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.

Унаследованные свойства и операции: Dom\Node. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Официальное описание Dom\EntityReference; точные современные сигнатуры сверены с PHP 8.5.11.

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\XMLDocument::createEntityReference.

Создать ссылку на внутреннюю сущностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea">]><catalog/>');
$entity = $document->createEntityReference("tea");
try {
    $document->documentElement->appendChild($entity);
} catch (DOMException $error) {
    echo "entity reference cannot be inserted by modern mutator
";
}
echo $entity->nodeName, "
";
Ожидаемый результат
entity reference cannot be inserted by modern mutator
tea

DTD принадлежит приложению; внешняя подгрузка не используется.

DOMProcessingInstruction: назначение, свойства и операции

ProcessingInstruction хранит командоподобную запись <?target data?> для получателя XML. PHP лишь хранит её target/data; инструкция xml-stylesheet сама не запускает XSLT. Modern класс наследует CharacterData, legacy имеет отдельные свойства data/target. Не помещайте в data закрывающую последовательность ?>.

Историческое семейство DOM, сохраняемое для совместимости. Новые возможности имеют отдельные пометки PHP8.3/8.4/8.5.

Место DOMProcessingInstruction в модели
  1. Документ хранит дерево XML/HTML
  2. Объект DOMProcessingInstruction представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMDocument::createProcessingInstruction.

Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$pi = $document->createProcessingInstruction("report", "version=1");
$document->insertBefore($pi, $document->documentElement);
echo $document->saveXml($pi), "
";
Ожидаемый результат
<?report version=1?>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Свойство этого классаЗначение и границы
$target: stringТолько чтение. Имя назначения ProcessingInstruction; например xml-stylesheet. Не запускает обработчик автоматически.
$data: stringТекст CharacterData/ProcessingInstruction. Изменяемый; индексы методов CharacterData в PHP считают Unicode code points UTF-8, не байты.

Унаследованные свойства и операции: DOMNode. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Все методы этого класса: DOMProcessingInstruction::__construct.

Официальное описание DOMProcessingInstruction; точные современные сигнатуры сверены с PHP 8.5.11.

Dom\ProcessingInstruction: назначение, свойства и операции

ProcessingInstruction хранит командоподобную запись <?target data?> для получателя XML. PHP лишь хранит её target/data; инструкция xml-stylesheet сама не запускает XSLT. Modern класс наследует CharacterData, legacy имеет отдельные свойства data/target. Не помещайте в data закрывающую последовательность ?>.

Современное семейство Dom\, PHP8.4+.

Место Dom\ProcessingInstruction в модели
  1. Документ хранит дерево XML/HTML
  2. Объект Dom\ProcessingInstruction представляет конкретную часть или операцию
  3. Методы и свойства ниже связывают эту часть с практическим действием

Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Document::createProcessingInstruction.

Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$pi = $document->createProcessingInstruction("report", "version=1");
$document->insertBefore($pi, $document->documentElement);
echo $document->saveXml($pi), "
";
Ожидаемый результат
<?report version=1?>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Свойство этого классаЗначение и границы
$target: stringТолько чтение. Имя назначения ProcessingInstruction; например xml-stylesheet. Не запускает обработчик автоматически.

Унаследованные свойства и операции: Dom\CharacterData. Они доступны у объекта этого класса; отдельная копия родителя не создаётся.

Интерфейсы: Dom\ChildNode.

Официальное описание Dom\ProcessingInstruction; точные современные сигнатуры сверены с PHP 8.5.11.

Свойства на практике: узлы, текст, разметка и сохранённая ссылка

Свойство читается через ->имя без скобок, метод вызывается со скобками. $root->firstChild может оказаться текстом перевода строки. firstElementChild сразу выбирает Element. Сравнивайте эти модели до того, как применять код к чужому форматированному файлу.

Пробелы тоже узлы
  1. catalog: Text перевода строки
  2. Затем product Tea, затем ещё Text
  3. firstChild → Text; firstElementChild → product
Отличить узлы от элементовСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog>
  <product>Tea</product>
</catalog>');
$root = $document->documentElement;
echo get_class($root->firstChild), "
";
echo $root->firstElementChild->localName, ":", $root->childElementCount, "
";
Ожидаемый результат
Dom\Text
product:1

Пробелы не являются ошибкой XML; они входят в childNodes и требуют правильного вида обхода.

Увидеть замену элемента и старую ссылкуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstElementChild;
$product->outerHTML = "<item>Coffee</item>";
echo $product->textContent, ":", $product->isConnected ? "attached" : "detached", "
";
echo $document->documentElement->firstElementChild->localName, "
";
Ожидаемый результат
Tea:detached
item

outerHTML заменил узел в дереве; переменная product всё ещё указывает на старый отсоединённый объект. PHP8.5+.

Отличить разметку от текстаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product/>');
$product = $document->documentElement;
$product->innerHTML = "<b>Tea</b>";
echo $product->textContent, "
";
$product->textContent = "<b>Tea</b>";
echo $document->saveXml($product), "
";
Ожидаемый результат
Tea
<product>&lt;b&gt;Tea&lt;/b&gt;</product>

Первое присваивание создаёт b; второе заменяет его буквальным текстом. Выбирайте по назначению данных.

Все константы DOM: типы узлов, ошибки и флаги

Тип nodeType — одно число; compareDocumentPosition — битовая маска; options парсера — другая маска; code исключения — категория ошибки. Нельзя произвольно объединять эти четыре назначения, даже если числовые значения совпадают. Константы доступны только с загруженным расширением.

КонстантаНазначение
XML_ELEMENT_NODE = 1Element: узел-тег
XML_ATTRIBUTE_NODE = 2Attr: поле элемента
XML_TEXT_NODE = 3Text: обычный текст
XML_CDATA_SECTION_NODE = 4CDATASection: текст с CDATA-сериализацией
XML_ENTITY_REF_NODE = 5EntityReference: ссылка на объявленную сущность
XML_ENTITY_NODE = 6Entity: объявление сущности
XML_PI_NODE = 7ProcessingInstruction: инструкция получателю
XML_COMMENT_NODE = 8Comment: комментарий
XML_DOCUMENT_NODE = 9Document: весь XML-документ
XML_DOCUMENT_TYPE_NODE = 10DocumentType: doctype
XML_DOCUMENT_FRAG_NODE = 11DocumentFragment: временная группа узлов
XML_NOTATION_NODE = 12Notation: DTD-нотация
XML_HTML_DOCUMENT_NODE = 13HTML Document старого libxml
XML_DTD_NODE = 14DTD-узел libxml
XML_ELEMENT_DECL_NODE = 15DTD-объявление элемента
XML_ATTRIBUTE_DECL_NODE = 16DTD-объявление атрибута
XML_ENTITY_DECL_NODE = 17DTD-объявление сущности
XML_NAMESPACE_DECL_NODE = 18Namespace-объявление legacy libxml
XML_LOCAL_NAMESPACE = 18Namespace-объявление legacy libxml
XML_ATTRIBUTE_CDATA = 1Строковое значение DTD-атрибута; не путайте с узлом CDATASection.
XML_ATTRIBUTE_ID = 2Уникальный идентификатор по DTD.
XML_ATTRIBUTE_IDREF = 3Ссылка на один ID.
XML_ATTRIBUTE_IDREFS = 4Список ссылок ID.
XML_ATTRIBUTE_ENTITY = 6Имя непарсенной сущности.
XML_ATTRIBUTE_NMTOKEN = 7Один XML name token.
XML_ATTRIBUTE_NMTOKENS = 8Список name tokens.
XML_ATTRIBUTE_ENUMERATION = 9Значение из перечисленных в DTD вариантов.
XML_ATTRIBUTE_NOTATION = 10Имя объявленной нотации.
DOM_PHP_ERR = 0Общая ошибка PHP DOM (историческая DOM_PHP_ERR).
DOM_INDEX_SIZE_ERR = 1Недопустимый индекс/размер CharacterData; проверьте единицы и границы.
DOMSTRING_SIZE_ERR = 2Исторический предел DOMString; не подмена лимита PHP memory_limit.
DOM_HIERARCHY_REQUEST_ERR = 3Нельзя разместить узел в таком месте: цикл, неподходящий тип/второй корень.
DOM_WRONG_DOCUMENT_ERR = 4Legacy узел принадлежит другому документу: importNode/adoptNode перед вставкой.
DOM_INVALID_CHARACTER_ERR = 5Недопустимые символы XML-имени/данных для конкретной операции.
DOM_NO_DATA_ALLOWED_ERR = 6Данные недопустимы для этого вида узла; исторический код.
DOM_NO_MODIFICATION_ALLOWED_ERR = 7Операция пытается изменить защищённую часть дерева.
DOM_NOT_FOUND_ERR = 8Переданный ребёнок/атрибут не принадлежит указанному владельцу.
DOM_NOT_SUPPORTED_ERR = 9Операция/тип узла не поддерживается; выбор другого метода нужен по смыслу.
DOM_INUSE_ATTRIBUTE_ERR = 10Attr уже используется другим Element; сначала отсоедините либо создайте копию.
DOM_INVALID_STATE_ERR = 11Объект в неподходящем состоянии для операции.
DOM_SYNTAX_ERR = 12Неправильный синтаксис выражения/фрагмента/позиции.
DOM_INVALID_MODIFICATION_ERR = 13Недопустимое изменение, например коллизия имени Attr или смена HTML namespace.
DOM_NAMESPACE_ERR = 14Неверная связь URI, префикса и квалифицированного имени.
DOM_INVALID_ACCESS_ERR = 15Исторический недопустимый доступ/параметр.
DOM_VALIDATION_ERR = 16Ошибка ограничений валидации DOM; не все schemaValidate failures обязательно используют исключение этого кода.
Dom\INDEX_SIZE_ERR = 1Недопустимый индекс/размер CharacterData; проверьте единицы и границы.
Dom\STRING_SIZE_ERR = 2Исторический предел DOMString; не подмена лимита PHP memory_limit.
Dom\HIERARCHY_REQUEST_ERR = 3Нельзя разместить узел в таком месте: цикл, неподходящий тип/второй корень.
Dom\WRONG_DOCUMENT_ERR = 4Legacy узел принадлежит другому документу: importNode/adoptNode перед вставкой.
Dom\INVALID_CHARACTER_ERR = 5Недопустимые символы XML-имени/данных для конкретной операции.
Dom\NO_DATA_ALLOWED_ERR = 6Данные недопустимы для этого вида узла; исторический код.
Dom\NO_MODIFICATION_ALLOWED_ERR = 7Операция пытается изменить защищённую часть дерева.
Dom\NOT_FOUND_ERR = 8Переданный ребёнок/атрибут не принадлежит указанному владельцу.
Dom\NOT_SUPPORTED_ERR = 9Операция/тип узла не поддерживается; выбор другого метода нужен по смыслу.
Dom\INUSE_ATTRIBUTE_ERR = 10Attr уже используется другим Element; сначала отсоедините либо создайте копию.
Dom\INVALID_STATE_ERR = 11Объект в неподходящем состоянии для операции.
Dom\SYNTAX_ERR = 12Неправильный синтаксис выражения/фрагмента/позиции.
Dom\INVALID_MODIFICATION_ERR = 13Недопустимое изменение, например коллизия имени Attr или смена HTML namespace.
Dom\NAMESPACE_ERR = 14Неверная связь URI, префикса и квалифицированного имени.
Dom\VALIDATION_ERR = 16Ошибка ограничений валидации DOM; не все schemaValidate failures обязательно используют исключение этого кода.
Dom\HTML_NO_DEFAULT_NS = 2147483648Флаг modern HTML parser: отключает HTML namespace и template-семантику. Не универсальное лечение пустого XPath; сначала registerNamespace.
Маска DOMNode / Dom\NodeСмысл
DOCUMENT_POSITION_DISCONNECTED = 1Узлы не имеют общего дерева.
DOCUMENT_POSITION_PRECEDING = 2Другой узел предшествует текущему.
DOCUMENT_POSITION_FOLLOWING = 4Другой узел следует после текущего.
DOCUMENT_POSITION_CONTAINS = 8Другой узел содержит текущий; читайте вместе с направлением сравнения.
DOCUMENT_POSITION_CONTAINED_BY = 16Другой узел находится внутри текущего.
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 32Порядок выбран реализацией для несвязанных/особых узлов; не превращайте его в бизнес-порядок.

У ошибки используйте $error->getCode(), у узла $node->nodeType; для проверки бита ($mask & FLAG) !== 0. Старые XML_ATTRIBUTE_* — категории DTD-атрибутов, а не способ получить тип PHP-переменной.

Ошибки входа, дерева и бизнес-правил: не скрывать частичный результат

Три границы
  1. XML parser: можно ли вообще построить дерево?
  2. DOM операция: допустимы ли имя/родитель/документ?
  3. Приложение: можно ли принять этот заказ/остаток?

libxml_use_internal_errors собирает сообщения parser, но не перехватывает ValueError/TypeError/DOMException PHP. Сохраняйте прежний режим и очищайте буфер после операции. Ошибка в конце не должна оставлять половину обновлённого каталога видимой: сначала собирайте проверенный результат, затем фиксируйте его как цельную версию.

Отклонить неправильный XML и восстановить обработчикСамостоятельный пример
<?php
declare(strict_types=1);

$previous = libxml_use_internal_errors(true);
libxml_clear_errors();
try {
    $document = new DOMDocument();
    if (!$document->loadXML("<catalog><product></catalog>")) {
        echo "catalog rejected
";
        echo count(libxml_get_errors()) > 0 ? "diagnostic recorded
" : "no diagnostic
";
    }
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
catalog rejected
diagnostic recorded

Наружу показан краткий статус, а не сырой XML с возможными чувствительными полями.

Разбор HTML/XML не даёт разрешение загружать ссылки, выполнять PHP callbacks, включать XInclude или показывать активную HTML-разметку посетителю. Каждая такая операция имеет собственную границу доверия. Для обычного XML-импорта не включайте внешние ресурсы только ради устранения предупреждения.

HTML-файл: найти товар через XPath и CSS, затем сохранить отчёт

HTML — отдельный формат разметки. В современном DOM обычный p получает namespace HTML: http://www.w3.org/1999/xhtml. Поэтому XPath без префикса может дать пустой результат. Сначала прочитаем один существующий товар двумя способами, затем заменим заголовок и сохраним файл. HTML-парсер здесь не исполняет JavaScript.

Исходный файл catalog.html
<!DOCTYPE html><html lang="ru"><head><meta charset="UTF-8"><title>Каталог</title></head><body><h1>Остатки</h1><p class="product">Чай — 4 шт.</p></body></html>
Как находится тот же p
  1. Файл содержит p.product с текстом Чай — 4 шт.
  2. XPath //p ищет без namespace: совпадения нет
  3. Префикс h связан с URI HTML: //h:p выбирает товар
  4. CSS p.product выбирает по тегу и метке class
  5. Изменение h1 → сохранённый catalog-report.html
Найти реальный товар, сохранив HTML namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromFile(__DIR__ . '/catalog.html');
$xpath = new Dom\XPath($document);
echo $xpath->query('//p')->length === 0 ? "Без URI: товар не найден\n" : "Найден\n";
$xpath->registerNamespace('h', 'http://www.w3.org/1999/xhtml');
echo 'XPath с URI: ', $xpath->query('//h:p')->item(0)->textContent, "\n";
echo 'CSS: ', $document->querySelector('p.product')->textContent, "\n";
Ожидаемый результат
Без URI: товар не найден
XPath с URI: Чай — 4 шт.
CSS: Чай — 4 шт.

h — наше сокращение в XPath, оно не обязано встречаться в исходном HTML. // ищет потомков, h:p означает элемент p в зарегистрированном URI. В CSS точка перед product означает токен class, а не namespace. Оба результата ссылаются на один реальный узел.

Заменить заголовок и прочитать сохранённый HTMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromFile(__DIR__ . '/catalog.html');
$document->querySelector('h1')->textContent = 'Склад на сегодня';
$path = __DIR__ . '/catalog-report.html';
if ($document->saveHtmlFile($path) === false) { throw new RuntimeException('Отчёт не записан'); }
echo file_get_contents($path);
Ожидаемый результат
<!DOCTYPE html><html lang="ru"><head><meta charset="UTF-8"><title>Каталог</title></head><body><h1>Склад на сегодня</h1><p class="product">Чай — 4 шт.</p></body></html>

Меняем текст h1 через textContent; названия товаров и структура остаются в дереве. Показаны байты настоящего файла. Для произвольного входного HTML нужен отдельный договор разрешённых тегов/атрибутов перед показом пользователям: DOM сам по себе не санитайзер.

Отключение namespace флагом HTML_NO_DEFAULT_NS меняет и поведение template, поэтому это не универсальная починка XPath. Выбирайте корректный запрос к сохранённой структуре. Обсуждение исходной проблемы: поиск //div в Dom\HTMLDocument; правило флага проверено по официальному контракту фабрики.

Законченная задача: выгрузить только доступные товары

Системе-получателю нужен stock-report.xml с товарами, остаток которых больше нуля. Используем уже знакомый catalog.xml. Источник сохраняем, поэтому создаём отдельный документ и копируем подходящий product через importNode. Его второй аргумент true включает name и stock: без глубокой копии потомки потерялись бы.

Результат отбора
  1. A1 Desk stock=3 → копия в stock-report
  2. B2 Lamp stock=0 → пропускаем
  3. Строка bad вместо остатка → прекращаем попытку до записи
  4. Временный файл готов → переименовываем в stock-report.xml

ctype_digit принимает здесь только непустую строку цифр; ограничение длины 6 задаёт учебный диапазон до 999999. (int) преобразует проверенную строку для сравнения. throw прерывает попытку при ошибке данных. rename завершает подготовку файла в той же учебной папке; для заменяемого рабочего файла правила замены зависят от файловой системы и должны быть определены приложением.

Получить stock-report.xml и сохранить исходные позицииСамостоятельный пример
<?php
declare(strict_types=1);

$source = Dom\XMLDocument::createFromFile(__DIR__ . '/catalog.xml');
$target = Dom\XMLDocument::createEmpty();
$root = $target->appendChild($target->createElement('stock-report'));
foreach ($source->getElementsByTagName('product') as $product) {
    $field = $product->getElementsByTagName('stock')->item(0);
    if ($field === null || !ctype_digit($field->textContent) || strlen($field->textContent) > 6) {
        throw new UnexpectedValueException('Недопустимый остаток товара ' . $product->getAttribute('sku'));
    }
    if ((int) $field->textContent > 0) {
        $root->appendChild($target->importNode($product, true));
    }
}
$pending = __DIR__ . '/stock-report.pending.xml';
$result = __DIR__ . '/stock-report.xml';
if ($target->saveXmlFile($pending) === false || !rename($pending, $result)) {
    throw new RuntimeException('Выгрузка не сохранена');
}
echo file_get_contents($result), "\n";
echo 'Источник по-прежнему содержит: ';
foreach ($source->getElementsByTagName('product') as $product) { echo $product->getAttribute('sku'), ' '; }
echo "\n";
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8"?>
<stock-report><product sku="A1"><name>Desk</name><stock>3</stock></product></stock-report>
Источник по-прежнему содержит: A1 B2 

Это законченная выгрузка небольшого согласованного каталога. В отличие от переноса adoptNode, importNode не забирает исходный товар. Проверены обязательность и целочисленный формат stock; уникальность SKU, допустимость названий и общий формат — отдельные требования, которые можно закрепить XSD и предметной проверкой.

Не создавать выгрузку при bad в остаткеСамостоятельный пример
<?php
declare(strict_types=1);

try { require __DIR__ . '/converter.php'; }
catch (UnexpectedValueException $error) { echo $error->getMessage(), "\n"; }
echo file_exists(__DIR__ . '/stock-report.xml') ? "Выгрузка есть\n" : "Выгрузка не создана: исправьте stock\n";
Ожидаемый результат
Недопустимый остаток товара A1
Выгрузка не создана: исправьте stock

converter.php совпадает с полным предыдущим обработчиком. Отрицательный сценарий запускается в отдельной папке с собственным каталогом; старых пользовательских файлов там нет. Ошибка проверяется до создания выгрузки.

Измените stock у Lamp с 0 на 2 и получите две позиции; замените 3 на bad и увидите отказ. Если вход слишком большой для полного дерева, такой отбор выполняйте через XMLReader; правила чтения узлов и проверки значений остаются явными.

Проблемы, которые разработчики встречают в реальном DOM-коде

Эти обсуждения дали постановки задач. Ответы могут относиться к старому PHP; правила и решения в этой главе сверены с официальным manual и PHP8.5.11, написаны и выполнены заново.

ОбсуждениеРазбор в нашем справочнике
Stack Overflow: 15272726При удалении из живого списка узлы пропускаются; в собственных примерах сначала snapshot ссылок, затем удаление. DOMNodeList::getIterator
Stack Overflow: 23619912Пустой XPath из-за default namespace; собственный префикс URI, не regex-удаление пространств имён. DOMXPath::registerNamespace
Stack Overflow: 24376808Амперсанд в legacy createElement.value; буквальный текст создаём через Text. DOMDocument::createTextNode
Stack Overflow: 42292655empty объекта не проверяет число совпадений; отдельно false при отказе, затем length/item(null). DOMXPath::query
Stack Overflow: 1759137cloneNode сохраняет документ-владелец; импорт в целевой документ создаёт совместимую копию. DOMDocument::importNode
Stack Overflow: 8218230Старая проблема входной кодировки HTML; для PHP8.4 modern factory с осмысленным overrideEncoding. Старые советы HTML-ENTITIES не копируются как универсальное решение. Dom\HTMLDocument::createFromString
Stack Overflow: 794437212025: HTML namespace modern DOM требует XPath-префикса; сохраняем стандартный namespace, не отключаем его без причины. Dom\XPath::registerNamespace
Stack Overflow: 793746662025: querySelector относится к современному Dom, а не старому DOMDocument. Объясняем выбор семейства. Dom\Document::querySelector

Полный указатель DOM: классы и все методы

Включены оба семейства, интерфейсы, служебные конструкторы, generated enum methods и мосты SimpleXML. Некоторые modern методы ещё не имеют отдельной страницы PHP.net: здесь они всё равно разобраны по tagged API PHP8.5.11 и поведению реализации. Выберите класс по задаче, затем конкретную операцию.

Класс/интерфейсВсе собственные методы
Dom\AdjacentPositionDom\AdjacentPosition::cases, Dom\AdjacentPosition::from, Dom\AdjacentPosition::tryFrom
DOMExceptionСобственные свойства и унаследованные операции описаны в классе.
DOMParentNodeDOMParentNode::append, DOMParentNode::prepend, DOMParentNode::replaceChildren
Dom\ParentNodeDom\ParentNode::append, Dom\ParentNode::prepend, Dom\ParentNode::replaceChildren, Dom\ParentNode::querySelector, Dom\ParentNode::querySelectorAll
DOMChildNodeDOMChildNode::remove, DOMChildNode::before, DOMChildNode::after, DOMChildNode::replaceWith
Dom\ChildNodeDom\ChildNode::remove, Dom\ChildNode::before, Dom\ChildNode::after, Dom\ChildNode::replaceWith
DOMImplementationDOMImplementation::hasFeature, DOMImplementation::createDocumentType, DOMImplementation::createDocument
Dom\ImplementationDom\Implementation::createDocumentType, Dom\Implementation::createDocument, Dom\Implementation::createHTMLDocument
DOMNodeDOMNode::appendChild, DOMNode::C14N, DOMNode::C14NFile, DOMNode::cloneNode, DOMNode::getLineNo, DOMNode::getNodePath, DOMNode::hasAttributes, DOMNode::hasChildNodes, DOMNode::insertBefore, DOMNode::isDefaultNamespace, DOMNode::isSameNode, DOMNode::isEqualNode, DOMNode::isSupported, DOMNode::lookupNamespaceURI, DOMNode::lookupPrefix, DOMNode::normalize, DOMNode::removeChild, DOMNode::replaceChild, DOMNode::contains, DOMNode::getRootNode, DOMNode::compareDocumentPosition, DOMNode::__sleep, DOMNode::__wakeup
Dom\NodeDom\Node::__construct, Dom\Node::getRootNode, Dom\Node::hasChildNodes, Dom\Node::normalize, Dom\Node::cloneNode, Dom\Node::isEqualNode, Dom\Node::isSameNode, Dom\Node::compareDocumentPosition, Dom\Node::contains, Dom\Node::lookupPrefix, Dom\Node::lookupNamespaceURI, Dom\Node::isDefaultNamespace, Dom\Node::insertBefore, Dom\Node::appendChild, Dom\Node::replaceChild, Dom\Node::removeChild, Dom\Node::getLineNo, Dom\Node::getNodePath, Dom\Node::C14N, Dom\Node::C14NFile, Dom\Node::__sleep, Dom\Node::__wakeup
DOMNameSpaceNodeDOMNameSpaceNode::__sleep, DOMNameSpaceNode::__wakeup
Dom\NamespaceInfoDom\NamespaceInfo::__construct
DOMDocumentFragmentDOMDocumentFragment::__construct, DOMDocumentFragment::appendXML, DOMDocumentFragment::append, DOMDocumentFragment::prepend, DOMDocumentFragment::replaceChildren
Dom\DocumentFragmentDom\DocumentFragment::appendXml, Dom\DocumentFragment::append, Dom\DocumentFragment::prepend, Dom\DocumentFragment::replaceChildren, Dom\DocumentFragment::querySelector, Dom\DocumentFragment::querySelectorAll
Dom\DocumentDom\Document::getElementsByTagName, Dom\Document::getElementsByTagNameNS, Dom\Document::getElementsByClassName, Dom\Document::createElement, Dom\Document::createElementNS, Dom\Document::createDocumentFragment, Dom\Document::createTextNode, Dom\Document::createCDATASection, Dom\Document::createComment, Dom\Document::createProcessingInstruction, Dom\Document::importNode, Dom\Document::adoptNode, Dom\Document::createAttribute, Dom\Document::createAttributeNS, Dom\Document::getElementById, Dom\Document::registerNodeClass, Dom\Document::schemaValidate, Dom\Document::schemaValidateSource, Dom\Document::relaxNgValidate, Dom\Document::relaxNgValidateSource, Dom\Document::append, Dom\Document::prepend, Dom\Document::replaceChildren, Dom\Document::importLegacyNode, Dom\Document::querySelector, Dom\Document::querySelectorAll
DOMDocumentDOMDocument::__construct, DOMDocument::createAttribute, DOMDocument::createAttributeNS, DOMDocument::createCDATASection, DOMDocument::createComment, DOMDocument::createDocumentFragment, DOMDocument::createElement, DOMDocument::createElementNS, DOMDocument::createEntityReference, DOMDocument::createProcessingInstruction, DOMDocument::createTextNode, DOMDocument::getElementById, DOMDocument::getElementsByTagName, DOMDocument::getElementsByTagNameNS, DOMDocument::importNode, DOMDocument::load, DOMDocument::loadXML, DOMDocument::normalizeDocument, DOMDocument::registerNodeClass, DOMDocument::save, DOMDocument::loadHTML, DOMDocument::loadHTMLFile, DOMDocument::saveHTML, DOMDocument::saveHTMLFile, DOMDocument::saveXML, DOMDocument::schemaValidate, DOMDocument::schemaValidateSource, DOMDocument::relaxNGValidate, DOMDocument::relaxNGValidateSource, DOMDocument::validate, DOMDocument::xinclude, DOMDocument::adoptNode, DOMDocument::append, DOMDocument::prepend, DOMDocument::replaceChildren
Dom\HTMLDocumentDom\HTMLDocument::createEmpty, Dom\HTMLDocument::createFromFile, Dom\HTMLDocument::createFromString, Dom\HTMLDocument::saveXml, Dom\HTMLDocument::saveXmlFile, Dom\HTMLDocument::saveHtml, Dom\HTMLDocument::saveHtmlFile
Dom\XMLDocumentDom\XMLDocument::createEmpty, Dom\XMLDocument::createFromFile, Dom\XMLDocument::createFromString, Dom\XMLDocument::createEntityReference, Dom\XMLDocument::validate, Dom\XMLDocument::xinclude, Dom\XMLDocument::saveXml, Dom\XMLDocument::saveXmlFile
DOMNodeListDOMNodeList::count, DOMNodeList::getIterator, DOMNodeList::item
Dom\NodeListDom\NodeList::count, Dom\NodeList::getIterator, Dom\NodeList::item
DOMNamedNodeMapDOMNamedNodeMap::getNamedItem, DOMNamedNodeMap::getNamedItemNS, DOMNamedNodeMap::item, DOMNamedNodeMap::count, DOMNamedNodeMap::getIterator
Dom\NamedNodeMapDom\NamedNodeMap::item, Dom\NamedNodeMap::getNamedItem, Dom\NamedNodeMap::getNamedItemNS, Dom\NamedNodeMap::count, Dom\NamedNodeMap::getIterator
Dom\DtdNamedNodeMapDom\DtdNamedNodeMap::item, Dom\DtdNamedNodeMap::getNamedItem, Dom\DtdNamedNodeMap::getNamedItemNS, Dom\DtdNamedNodeMap::count, Dom\DtdNamedNodeMap::getIterator
Dom\HTMLCollectionDom\HTMLCollection::item, Dom\HTMLCollection::namedItem, Dom\HTMLCollection::count, Dom\HTMLCollection::getIterator
DOMCharacterDataDOMCharacterData::appendData, DOMCharacterData::substringData, DOMCharacterData::insertData, DOMCharacterData::deleteData, DOMCharacterData::replaceData, DOMCharacterData::replaceWith, DOMCharacterData::remove, DOMCharacterData::before, DOMCharacterData::after
Dom\CharacterDataDom\CharacterData::substringData, Dom\CharacterData::appendData, Dom\CharacterData::insertData, Dom\CharacterData::deleteData, Dom\CharacterData::replaceData, Dom\CharacterData::remove, Dom\CharacterData::before, Dom\CharacterData::after, Dom\CharacterData::replaceWith
DOMAttrDOMAttr::__construct, DOMAttr::isId
Dom\AttrDom\Attr::isId, Dom\Attr::rename
DOMElementDOMElement::__construct, DOMElement::getAttribute, DOMElement::getAttributeNames, DOMElement::getAttributeNS, DOMElement::getAttributeNode, DOMElement::getAttributeNodeNS, DOMElement::getElementsByTagName, DOMElement::getElementsByTagNameNS, DOMElement::hasAttribute, DOMElement::hasAttributeNS, DOMElement::removeAttribute, DOMElement::removeAttributeNS, DOMElement::removeAttributeNode, DOMElement::setAttribute, DOMElement::setAttributeNS, DOMElement::setAttributeNode, DOMElement::setAttributeNodeNS, DOMElement::setIdAttribute, DOMElement::setIdAttributeNS, DOMElement::setIdAttributeNode, DOMElement::toggleAttribute, DOMElement::remove, DOMElement::before, DOMElement::after, DOMElement::replaceWith, DOMElement::append, DOMElement::prepend, DOMElement::replaceChildren, DOMElement::insertAdjacentElement, DOMElement::insertAdjacentText
Dom\ElementDom\Element::hasAttributes, Dom\Element::getAttributeNames, Dom\Element::getAttribute, Dom\Element::getAttributeNS, Dom\Element::setAttribute, Dom\Element::setAttributeNS, Dom\Element::removeAttribute, Dom\Element::removeAttributeNS, Dom\Element::toggleAttribute, Dom\Element::hasAttribute, Dom\Element::hasAttributeNS, Dom\Element::getAttributeNode, Dom\Element::getAttributeNodeNS, Dom\Element::setAttributeNode, Dom\Element::setAttributeNodeNS, Dom\Element::removeAttributeNode, Dom\Element::getElementsByTagName, Dom\Element::getElementsByTagNameNS, Dom\Element::getElementsByClassName, Dom\Element::insertAdjacentElement, Dom\Element::insertAdjacentText, Dom\Element::insertAdjacentHTML, Dom\Element::setIdAttribute, Dom\Element::setIdAttributeNS, Dom\Element::setIdAttributeNode, Dom\Element::remove, Dom\Element::before, Dom\Element::after, Dom\Element::replaceWith, Dom\Element::append, Dom\Element::prepend, Dom\Element::replaceChildren, Dom\Element::querySelector, Dom\Element::querySelectorAll, Dom\Element::closest, Dom\Element::matches, Dom\Element::getInScopeNamespaces, Dom\Element::getDescendantNamespaces, Dom\Element::rename
Dom\HTMLElementСобственные свойства и унаследованные операции описаны в классе.
DOMTextDOMText::__construct, DOMText::isWhitespaceInElementContent, DOMText::isElementContentWhitespace, DOMText::splitText
Dom\TextDom\Text::splitText
DOMCommentDOMComment::__construct
Dom\CommentСобственные свойства и унаследованные операции описаны в классе.
DOMCdataSectionDOMCdataSection::__construct
Dom\CDATASectionСобственные свойства и унаследованные операции описаны в классе.
DOMDocumentTypeСобственные свойства и унаследованные операции описаны в классе.
Dom\DocumentTypeDom\DocumentType::remove, Dom\DocumentType::before, Dom\DocumentType::after, Dom\DocumentType::replaceWith
DOMNotationСобственные свойства и унаследованные операции описаны в классе.
Dom\NotationСобственные свойства и унаследованные операции описаны в классе.
DOMEntityСобственные свойства и унаследованные операции описаны в классе.
Dom\EntityСобственные свойства и унаследованные операции описаны в классе.
DOMEntityReferenceDOMEntityReference::__construct
Dom\EntityReferenceСобственные свойства и унаследованные операции описаны в классе.
DOMProcessingInstructionDOMProcessingInstruction::__construct
Dom\ProcessingInstructionСобственные свойства и унаследованные операции описаны в классе.
DOMXPathDOMXPath::__construct, DOMXPath::evaluate, DOMXPath::query, DOMXPath::registerNamespace, DOMXPath::registerPhpFunctions, DOMXPath::registerPhpFunctionNS, DOMXPath::quote
Dom\XPathDom\XPath::__construct, Dom\XPath::evaluate, Dom\XPath::query, Dom\XPath::registerNamespace, Dom\XPath::registerPhpFunctions, Dom\XPath::registerPhpFunctionNS, Dom\XPath::quote
Dom\TokenListDom\TokenList::__construct, Dom\TokenList::item, Dom\TokenList::contains, Dom\TokenList::add, Dom\TokenList::remove, Dom\TokenList::toggle, Dom\TokenList::replace, Dom\TokenList::supports, Dom\TokenList::count, Dom\TokenList::getIterator

Функции-мосты: dom_import_simplexml, Dom\import_simplexml.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

DOMNode::appendChildДобавить строку заказа последним ребёнком: создание узла и его включение в документ — два разных действия.

PHP 5+ · dom

DOMNode::appendChild(DOMNode $node): DOMNode|false

Добавить строку заказа последним ребёнком: создание узла и его включение в документ — два разных действия.

New DOMDocument создаёт документ; loadXML заполняет его известной правильной XML-строкой. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Задача первого примера — включить ранее созданный товар в order. Пока createElement только вернул отдельный узел, заказ остаётся пустым. AppendChild вызывается у будущего родителя, возвращает вставленный узел и связывает его с деревом.

Аргумент / поведениеПолный контракт
$nodeГотовый узел. Если он уже находится в дереве, перемещается: одновременно двух родителей у одного узла нет. DocumentFragment отдаёт своих детей, а сам остаётся пустым.
ВозвратДобавленный узел или false при неуспехе; не путать с исключениями ниже. Для фрагмента результат не является отдельной строкой заказа; проверяйте детей родителя.
Ошибки структурыDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Семейство объектовАргументы принадлежат старому DOM* API. Смешение семейств вызывает TypeError; сходство имён не делает их взаимозаменяемыми.
Добавление строки
  1. createElement: отдельный item без родителя
  2. appendChild(item): связь order → item
  3. В документе появился ребёнок; переменная item указывает на тот же узел
Добавить товар к заказу и использовать возвращённый узелСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order/>');
$order = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'Book';
echo 'До вставки: ', $document->saveXml($order), "\n";
$added = $order->appendChild($item);
$added->setAttribute('sku', 'B1');
echo 'После вставки: ', $document->saveXml($order), "\n";
Ожидаемый результат
До вставки: <order/>
После вставки: <order><item sku="B1">Book</item></order>

CreateElement создал отдельный item; запись textContent добавила его текст Book. До appendChild заказ ещё пуст. Метод вызывается у order, получает объект item и возвращает этот же добавленный узел в added. Поэтому setAttribute через added присваивает sku=B1 товару внутри заказа. Изменение видно в полной XML-строке; второй товар от присваивания не появился.

Перенести товар из ожидающих в готовыеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<shop><pending><item>Book</item></pending><ready/></shop>');
$shop = $document->documentElement;
$pending = $shop->firstElementChild;
$ready = $pending->nextElementSibling;
$item = $pending->firstElementChild;
$ready->appendChild($item);
echo $document->saveXml($shop), "\n";
echo 'Теперь товар в: ', $item->parentNode->nodeName, "\n";
Ожидаемый результат
<shop><pending/><ready><item>Book</item></ready></shop>
Теперь товар в: ready

FirstElementChild берёт первого ребёнка-элемент, nextElementSibling —следующего соседа-элемент. Ready получает существующий item: appendChild переносит его, не копирует. Pending стал пустым, parentNode у item теперь ready. Если нужно сохранить товар в обоих списках, сначала создайте отдельную копию cloneNode(true).

Связанные операции: DOMNode::cloneNode, DOMNode::insertBefore, DOMDocument::importNode.

DOMNode::insertBeforeВставить предупреждение перед первой позицией или новый пункт перед известным соседом, не пересобирая весь XML.

PHP 5+ · dom

DOMNode::insertBefore(DOMNode $node, ?DOMNode $child = null): DOMNode|false

Вставить предупреждение перед первой позицией или новый пункт перед известным соседом, не пересобирая весь XML.

New DOMDocument создаёт документ; loadXML заполняет его известной правильной XML-строкой. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. У order есть товар; нужно добавить заметку перед ним. Сначала сохраняем объект товара в item, создаём отдельный notice, затем передаём оба объекта в insertBefore. Возвращённый inserted обозначает заметку. Следующий пример с очередью отдельно показывает null как конец списка.

Аргумент / поведениеПолный контракт
$nodeДобавляемый или перемещаемый узел. Для фрагмента вставляются его дети в прежнем порядке.
$childНепосредственный ребёнок текущего родителя: точка вставки ПЕРЕД ним. null означает конец списка. В старом API можно опустить: значение по умолчанию null.
ВозвратДобавленный узел либо false при неуспехе.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении; DOM_NOT_FOUND_ERR для чужого опорного/удаляемого ребёнка. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Вставка перед соседом
  1. order: item-A → item-B
  2. insertBefore(notice, item-B)
  3. order: item-A → notice → item-B
Поставить примечание перед товаромСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order><item>Book</item></order>');
$order = $document->documentElement;
$item = $order->firstChild;
$notice = $document->createElement('notice');
$notice->textContent = 'Check address';
$inserted = $order->insertBefore($notice, $item);
echo 'Вставлено: ', $inserted->textContent, "\n";
echo $document->saveXml($order), "\n";
Ожидаемый результат
Вставлено: Check address
<order><notice>Check address</notice><item>Book</item></order>

Item —объект существующего ребёнка, а не строка item. Первый аргумент notice задаёт новый узел, второй item —соседа справа от него. Return попадает в inserted, откуда читается текст добавленной заметки. В этом XML нет пробельных Text между тегами; если они есть, firstChild может оказаться текстом, и для элемента удобнее firstElementChild.

Очередь: вставить срочный пункт и явно добавить обычный в конецСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<queue><task>normal</task></queue>');
$queue = $document->documentElement;
$urgent = $document->createElement('task');
$urgent->textContent = 'urgent';
$queue->insertBefore($urgent, $queue->firstChild);
$last = $document->createElement('task');
$last->textContent = 'later';
$queue->insertBefore($last, null);
foreach ($queue->childNodes as $task) {
    echo $task->textContent, "
";
}
Ожидаемый результат
urgent
normal
later

null — осознанное отсутствие опорного узла. Чтобы вставить после найденного узла, можно передать его nextSibling; если он последний, это как раз null.

Связанные операции: DOMNode::appendChild, DOMNode::replaceChild.

DOMNode::replaceChildЗаменить устаревший блок цены новым, сохранив место блока и получив прежнюю версию для журнала изменений.

PHP 5+ · dom

DOMNode::replaceChild(DOMNode $node, DOMNode $child): DOMNode|false

Заменить устаревший блок цены новым, сохранив место блока и получив прежнюю версию для журнала изменений.

New DOMDocument создаёт документ; loadXML заполняет его известной правильной XML-строкой. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Для замены цены создаём newPrice, а старую цену получаем как первого ребёнка product. Метод возвращает снятый узел; присваивание oldPrice не получает новую цену. Это позволяет сформировать журнал «было100, стало120».

Аргумент / поведениеПолный контракт
$nodeНовый узел; при существующем родителе переносится. Фрагмент заменяет старого ребёнка последовательностью своих детей.
$childСтарый непосредственный ребёнок, который нужно убрать.
ВозвратИменно УДАЛЁННЫЙ старый узел, а не новый; возможен false при неуспехе.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении; DOM_NOT_FOUND_ERR для чужого опорного/удаляемого ребёнка. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Замена цены
  1. product → price 100
  2. replaceChild(newPrice, oldPrice)
  3. product → price 120; результат вызова — отсоединённый oldPrice 100
Заменить цену и сохранить старую запись для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product><price>100</price></product>');
$product = $document->documentElement;
$newPrice = $document->createElement('price');
$newPrice->textContent = '120';
$oldPrice = $product->replaceChild($newPrice, $product->firstChild);
echo 'Товар после правки: ', $document->saveXml($product), "\n";
echo 'Снятая версия: ', $document->saveXml($oldPrice), "\n";
Ожидаемый результат
Товар после правки: <product><price>120</price></product>
Снятая версия: <price>100</price>

Аргументы идут в порядке новый, старый. Но return —именно снятый старый price100, сохранённый в oldPrice. SaveXml может прочитать его и после отсоединения. Новая цена120 уже в product. Старый узел остался связан с ownerDocument, но больше не является ребёнком product; ниже этот возврат позволяет откатить локальную правку.

Откатить локальную правку, если проверка бизнес-правила не прошлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product><price>100</price></product>');
$product = $document->documentElement;
$replacement = $document->createElement('price');
$replacement->textContent = '-20';
$old = $product->replaceChild($replacement, $product->firstChild);
if ((int) $replacement->textContent < 0) {
    $product->replaceChild($old, $replacement);
}
echo $product->textContent, "
";
Ожидаемый результат
100

Это откат только двух узлов в памяти, не транзакция базы данных и не общая отмена произвольного импорта. Практичнее проверять цену до мутации; пример показывает, какой объект возвращён и как его вернуть.

Связанные операции: DOMNode::insertBefore, DOMNode::removeChild.

DOMNode::removeChildИсключить позицию из текущего заказа: удалить связь с родителем, сохранив сам узел для архива или переноса.

PHP 5+ · dom

DOMNode::removeChild(DOMNode $child): DOMNode|false

Исключить позицию из текущего заказа: удалить связь с родителем, сохранив сам узел для архива или переноса.

New DOMDocument создаёт документ; loadXML заполняет его известной правильной XML-строкой. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Передавайте непосредственного ребёнка того объекта, у которого вызывается removeChild. Возвращённый removed можно прочитать или вставить в другой элемент. Первое действие ниже удаляет только связь с заказом; последующее отдельно архивирует тот же узел.

Аргумент / поведениеПолный контракт
$childНепосредственный ребёнок этого родителя. Внук не подходит: удалять его нужно у собственного parentNode.
ВозвратУдалённый узел либо false при неуспехе. Его parentNode становится null, ownerDocument сохраняется.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении; DOM_NOT_FOUND_ERR для чужого опорного/удаляемого ребёнка. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Живые спискиchildNodes и getElementsByTagName меняются во время удаления. Увеличение индекса после removeChild часто пропускает следующего ребёнка. Используйте while(firstChild), проход с конца или заранее сохранённый список объектов.
Удаление — разрыв связи
  1. order → item; переменная item держит объект
  2. order.removeChild(item)
  3. order пуст; item по-прежнему доступен в переменной
Убрать товар из заказа и перенести его в архивСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<shop><order><item>Book</item></order><archive/></shop>');
$shop = $document->documentElement;
$order = $shop->firstElementChild;
$archive = $order->nextElementSibling;
$removed = $order->removeChild($order->firstChild);
echo 'Удалённый товар: ', $removed->textContent, "\n";
echo 'После удаления: ', $document->saveXml($shop), "\n";
$archive->appendChild($removed);
echo 'После архивирования: ', $document->saveXml($shop), "\n";
Ожидаемый результат
Удалённый товар: Book
После удаления: <shop><order/><archive/></shop>
После архивирования: <shop><order/><archive><item>Book</item></archive></shop>

RemoveChild разорвал связь order→item и вернул живой объект в removed. Текст ещё доступен; второй вызов appendChild использует возвращённый узел в архиве того же документа. Удаление из дерева не уничтожило данные. Так различаются удалённая связь, оставшийся PHP-объект и новая связь archive→item.

Очистить корзину, сохранив названия всех снятых позицийСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<cart><item>A</item><item>B</item><item>C</item></cart>');
$cart = $document->documentElement;
$removedNames = [];
while ($cart->firstChild !== null) {
    $removed = $cart->removeChild($cart->firstChild);
    $removedNames[] = $removed->textContent;
}
echo 'Сняты позиции: ', implode(', ', $removedNames), "\n";
echo 'Корзина: ', $document->saveXml($cart), "\n";
Ожидаемый результат
Сняты позиции: A, B, C
Корзина: <cart/>

While каждый раз заново читает первого оставшегося ребёнка: сначалаA, затемB, затемC. Присваивание [] добавляет его название в массив; implode соединяет названия для журнала. После C firstChild равен null и цикл заканчивается. При увеличении индекса живой коллекции следующая позиция могла бы сдвинуться на уже пройденный индекс.

Связанные операции: DOMNode::appendChild, DOMNode::replaceChild.

DOMNode::cloneNodeПолучить независимую заготовку карточки товара: исходный узел остаётся на месте, а копию можно изменить и затем вставить.

PHP 5+ · dom

DOMNode::cloneNode(bool $deep = false): DOMNode|false

Получить независимую заготовку карточки товара: исходный узел остаётся на месте, а копию можно изменить и затем вставить.

New DOMDocument создаёт документ; loadXML заполняет его известной правильной XML-строкой. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. CloneNode возвращает отдельный узел. Первый пример прямо сопоставляет XML двух копий: отсутствие name видно без подсчёта детей. Второй применяет глубокую копию как заготовку товараB и оставляет товарA прежним.

Аргумент / поведениеПолный контракт
$deep = falsefalse копирует сам узел; true рекурсивно копирует потомков. Атрибуты элемента копируются даже при false. У копии нет parentNode.
ВозвратНовый Node или false при невозможности клонирования. Копия обычного узла принадлежит прежнему ownerDocument; это не импорт в другой документ.
Идентификаторы и ограниченияid и другие атрибуты тоже копируются: приложение должно выдать новый id, если он должен быть уникален. Копируются DOM-данные, а не произвольная история PHP-объекта. Для другого документа используйте importNode; прямой appendChild чужого узла даёт WRONG_DOCUMENT_ERR.
Два товара из одного шаблона
  1. Исходник: item id=A → name=Book
  2. cloneNode(true): второй item id=A → отдельный name=Book
  3. Изменить id=B и текст только у копии; затем appendChild
Увидеть, что скопировано без детей и вместе с нимиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<item sku="A"><name>Book</name></item>');
$item = $document->documentElement;
$shallow = $item->cloneNode();
$deep = $item->cloneNode(true);
echo 'Без детей: ', $document->saveXml($shallow), "\n";
echo 'С детьми: ', $document->saveXml($deep), "\n";
echo 'Исходник: ', $document->saveXml($item), "\n";
Ожидаемый результат
Без детей: <item sku="A"/>
С детьми: <item sku="A"><name>Book</name></item>
Исходник: <item sku="A"><name>Book</name></item>

Без аргумента deep=false: атрибут sku скопирован, ребёнок name и его Text Book —нет. True копирует всё поддерево. Оба return —отдельные узлы без родителя; сохранение их строкой не вставляет копии в исходный документ. Чтобы создать второй товар в каталоге, нужно отдельно изменить id и вызвать appendChild, как ниже.

Создать второй товар и не изменить первыйСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item id="A"><name>Book</name></item></catalog>');
$catalog = $document->documentElement;
$copy = $catalog->firstElementChild->cloneNode(true);
$copy->setAttribute('id', 'B');
$copy->firstElementChild->textContent = 'Notebook';
$catalog->appendChild($copy);
foreach ($catalog->childNodes as $item) {
    echo $item->getAttribute('id'), ':', $item->textContent, "
";
}
Ожидаемый результат
A:Book
B:Notebook

Здесь копия глубока относительно DOM-дерева. Это другой механизм, чем обычный PHP clone с вложенными пользовательскими объектами; правила свойств PHP-класса не подменяют правила DOM.

Связанные операции: DOMNode::isSameNode, DOMNode::isEqualNode, DOMDocument::importNode.

DOMNode::normalizeПосле нескольких добавлений текста собрать соседние текстовые узлы в один, чтобы обход документа не зависел от истории его редактирования.

PHP 5+ · dom

DOMNode::normalize(): void

После нескольких добавлений текста собрать соседние текстовые узлы в один, чтобы обход документа не зависел от истории его редактирования.

New DOMDocument создаёт документ; loadXML заполняет его известной правильной XML-строкой. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Иногда видимая строка AB построена двумя соседними Text. Normalize объединяет их на месте, не меняя саму строку. Первый опыт специально показывает одновременно число Text и их общее содержимое —это различие структуры при одинаковом тексте. Затем видна граница вложенного strong.

Аргумент / поведениеПолный контракт
АргументыНет. Обрабатывается текущий узел и его поддерево.
ИзменениеСоседние Text объединяются, пустые текстовые узлы удаляются. Граница элемента остаётся границей: текст по разные стороны вложенного элемента не склеивается в один узел.
Возвратvoid: полезный результат находится в изменённом дереве. Не присваивайте результат обратно переменной узла.
ГраницыЭто не проверка XML по схеме, не санитайзер HTML, не удаление пробелов и не Unicode-нормализация NFC. Пробел с текстом остаётся значимым содержимым.
Сборка текстовых фрагментов
  1. label → Text("A") → Text("") → Text("B")
  2. label.normalize()
  3. label → Text("AB"); видимый текст прежний, узлов меньше
Склеить два добавленных фрагментаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label/>');
$label = $document->documentElement;
$label->appendChild($document->createTextNode('A'));
$label->appendChild($document->createTextNode('B'));
echo $label->childNodes->length, ':', $label->textContent, "
";
$label->normalize();
echo $label->childNodes->length, ':', $label->textContent, "
";
Ожидаемый результат
2:AB
1:AB

Один и тот же видимый текст может состоять из разного числа узлов. Нормализация убирает эту зависимость для соседних текстовых фрагментов.

Собрать текст, сохранив выделение в описанииСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<description/>');
$description = $document->documentElement;
$description->appendChild($document->createTextNode('Buy '));
$strong = $description->appendChild($document->createElement('strong'));
$strong->appendChild($document->createTextNode('A'));
$strong->appendChild($document->createTextNode('B'));
$description->appendChild($document->createTextNode(' now'));
$description->normalize();
echo $document->saveXml($description), "\n";
echo 'Текст посетителю: ', $description->textContent, "\n";
echo 'Узлов внутри strong: ', $strong->childNodes->length, "\n";
Ожидаемый результат
<description>Buy <strong>AB</strong> now</description>
Текст посетителю: Buy AB now
Узлов внутри strong: 1

Первый appendChild элемента возвращает strong; следующие вызовы создают два его соседних Text. Normalize возвращает void —результат нужно читать в дереве, не присваивать обратно description. Внутри strong остался один Text AB; элементstrong и пробелы Buy/now сохранены. Число1 здесь измеряет конкретный эффект склейки, а вся XML-строка показывает неизменённую разметку.

Связанные операции: DOMNode::hasChildNodes, DOMText::splitText.

Dom\Node::appendChildДобавить строку заказа последним ребёнком: создание узла и его включение в документ — два разных действия.

PHP 8.4+ · dom

Dom\Node::appendChild(Dom\Node $node): Dom\Node

Добавить строку заказа последним ребёнком: создание узла и его включение в документ — два разных действия.

Dom\XMLDocument::createFromString читает известную XML-строку и возвращает документ в document. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Задача первого примера — включить ранее созданный товар в order. Пока createElement только вернул отдельный узел, заказ остаётся пустым. AppendChild вызывается у будущего родителя, возвращает вставленный узел и связывает его с деревом.

Аргумент / поведениеПолный контракт
$nodeГотовый узел. Если он уже находится в дереве, перемещается: одновременно двух родителей у одного узла нет. DocumentFragment отдаёт своих детей, а сам остаётся пустым.
ВозвратДобавленный узел. Для фрагмента результат не является отдельной строкой заказа; проверяйте детей родителя.
Ошибки структурыDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Семейство объектовАргументы принадлежат современному Dom\* API. Смешение семейств вызывает TypeError; сходство имён не делает их взаимозаменяемыми.
Добавление строки
  1. createElement: отдельный item без родителя
  2. appendChild(item): связь order → item
  3. В документе появился ребёнок; переменная item указывает на тот же узел
Добавить товар к заказу и использовать возвращённый узелСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order/>');
$order = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'Book';
echo 'До вставки: ', $document->saveXml($order), "\n";
$added = $order->appendChild($item);
$added->setAttribute('sku', 'B1');
echo 'После вставки: ', $document->saveXml($order), "\n";
Ожидаемый результат
До вставки: <order/>
После вставки: <order><item sku="B1">Book</item></order>

CreateElement создал отдельный item; запись textContent добавила его текст Book. До appendChild заказ ещё пуст. Метод вызывается у order, получает объект item и возвращает этот же добавленный узел в added. Поэтому setAttribute через added присваивает sku=B1 товару внутри заказа. Изменение видно в полной XML-строке; второй товар от присваивания не появился.

Перенести товар из ожидающих в готовыеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<shop><pending><item>Book</item></pending><ready/></shop>');
$shop = $document->documentElement;
$pending = $shop->firstElementChild;
$ready = $pending->nextElementSibling;
$item = $pending->firstElementChild;
$ready->appendChild($item);
echo $document->saveXml($shop), "\n";
echo 'Теперь товар в: ', $item->parentNode->nodeName, "\n";
Ожидаемый результат
<shop><pending/><ready><item>Book</item></ready></shop>
Теперь товар в: ready

FirstElementChild берёт первого ребёнка-элемент, nextElementSibling —следующего соседа-элемент. Ready получает существующий item: appendChild переносит его, не копирует. Pending стал пустым, parentNode у item теперь ready. Если нужно сохранить товар в обоих списках, сначала создайте отдельную копию cloneNode(true).

Связанные операции: Dom\Node::cloneNode, Dom\Node::insertBefore, Dom\Document::importNode.

Dom\Node::insertBeforeВставить предупреждение перед первой позицией или новый пункт перед известным соседом, не пересобирая весь XML.

PHP 8.4+ · dom

Dom\Node::insertBefore(Dom\Node $node, ?Dom\Node $child): Dom\Node

Вставить предупреждение перед первой позицией или новый пункт перед известным соседом, не пересобирая весь XML.

Dom\XMLDocument::createFromString читает известную XML-строку и возвращает документ в document. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. У order есть товар; нужно добавить заметку перед ним. Сначала сохраняем объект товара в item, создаём отдельный notice, затем передаём оба объекта в insertBefore. Возвращённый inserted обозначает заметку. Следующий пример с очередью отдельно показывает null как конец списка.

Аргумент / поведениеПолный контракт
$nodeДобавляемый или перемещаемый узел. Для фрагмента вставляются его дети в прежнем порядке.
$childНепосредственный ребёнок текущего родителя: точка вставки ПЕРЕД ним. null означает конец списка. В современном API второй аргумент обязателен, даже когда передаёте null.
ВозвратДобавленный узел.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа; NOT_FOUND_ERR, если опорный/удаляемый ребёнок не принадлежит указанному родителю. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Вставка перед соседом
  1. order: item-A → item-B
  2. insertBefore(notice, item-B)
  3. order: item-A → notice → item-B
Поставить примечание перед товаромСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order><item>Book</item></order>');
$order = $document->documentElement;
$item = $order->firstChild;
$notice = $document->createElement('notice');
$notice->textContent = 'Check address';
$inserted = $order->insertBefore($notice, $item);
echo 'Вставлено: ', $inserted->textContent, "\n";
echo $document->saveXml($order), "\n";
Ожидаемый результат
Вставлено: Check address
<order><notice>Check address</notice><item>Book</item></order>

Item —объект существующего ребёнка, а не строка item. Первый аргумент notice задаёт новый узел, второй item —соседа справа от него. Return попадает в inserted, откуда читается текст добавленной заметки. В этом XML нет пробельных Text между тегами; если они есть, firstChild может оказаться текстом, и для элемента удобнее firstElementChild.

Очередь: вставить срочный пункт и явно добавить обычный в конецСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<queue><task>normal</task></queue>');
$queue = $document->documentElement;
$urgent = $document->createElement('task');
$urgent->textContent = 'urgent';
$queue->insertBefore($urgent, $queue->firstChild);
$last = $document->createElement('task');
$last->textContent = 'later';
$queue->insertBefore($last, null);
foreach ($queue->childNodes as $task) {
    echo $task->textContent, "
";
}
Ожидаемый результат
urgent
normal
later

null — осознанное отсутствие опорного узла. Чтобы вставить после найденного узла, можно передать его nextSibling; если он последний, это как раз null.

Связанные операции: Dom\Node::appendChild, Dom\Node::replaceChild.

Dom\Node::replaceChildЗаменить устаревший блок цены новым, сохранив место блока и получив прежнюю версию для журнала изменений.

PHP 8.4+ · dom

Dom\Node::replaceChild(Dom\Node $node, Dom\Node $child): Dom\Node

Заменить устаревший блок цены новым, сохранив место блока и получив прежнюю версию для журнала изменений.

Dom\XMLDocument::createFromString читает известную XML-строку и возвращает документ в document. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Для замены цены создаём newPrice, а старую цену получаем как первого ребёнка product. Метод возвращает снятый узел; присваивание oldPrice не получает новую цену. Это позволяет сформировать журнал «было100, стало120».

Аргумент / поведениеПолный контракт
$nodeНовый узел; при существующем родителе переносится. Фрагмент заменяет старого ребёнка последовательностью своих детей.
$childСтарый непосредственный ребёнок, который нужно убрать.
ВозвратИменно УДАЛЁННЫЙ старый узел, а не новый.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа; NOT_FOUND_ERR, если опорный/удаляемый ребёнок не принадлежит указанному родителю. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Замена цены
  1. product → price 100
  2. replaceChild(newPrice, oldPrice)
  3. product → price 120; результат вызова — отсоединённый oldPrice 100
Заменить цену и сохранить старую запись для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product><price>100</price></product>');
$product = $document->documentElement;
$newPrice = $document->createElement('price');
$newPrice->textContent = '120';
$oldPrice = $product->replaceChild($newPrice, $product->firstChild);
echo 'Товар после правки: ', $document->saveXml($product), "\n";
echo 'Снятая версия: ', $document->saveXml($oldPrice), "\n";
Ожидаемый результат
Товар после правки: <product><price>120</price></product>
Снятая версия: <price>100</price>

Аргументы идут в порядке новый, старый. Но return —именно снятый старый price100, сохранённый в oldPrice. SaveXml может прочитать его и после отсоединения. Новая цена120 уже в product. Старый узел остался связан с ownerDocument, но больше не является ребёнком product; ниже этот возврат позволяет откатить локальную правку.

Откатить локальную правку, если проверка бизнес-правила не прошлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product><price>100</price></product>');
$product = $document->documentElement;
$replacement = $document->createElement('price');
$replacement->textContent = '-20';
$old = $product->replaceChild($replacement, $product->firstChild);
if ((int) $replacement->textContent < 0) {
    $product->replaceChild($old, $replacement);
}
echo $product->textContent, "
";
Ожидаемый результат
100

Это откат только двух узлов в памяти, не транзакция базы данных и не общая отмена произвольного импорта. Практичнее проверять цену до мутации; пример показывает, какой объект возвращён и как его вернуть.

Связанные операции: Dom\Node::insertBefore, Dom\Node::removeChild.

Dom\Node::removeChildИсключить позицию из текущего заказа: удалить связь с родителем, сохранив сам узел для архива или переноса.

PHP 8.4+ · dom

Dom\Node::removeChild(Dom\Node $child): Dom\Node

Исключить позицию из текущего заказа: удалить связь с родителем, сохранив сам узел для архива или переноса.

Dom\XMLDocument::createFromString читает известную XML-строку и возвращает документ в document. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Передавайте непосредственного ребёнка того объекта, у которого вызывается removeChild. Возвращённый removed можно прочитать или вставить в другой элемент. Первое действие ниже удаляет только связь с заказом; последующее отдельно архивирует тот же узел.

Аргумент / поведениеПолный контракт
$childНепосредственный ребёнок этого родителя. Внук не подходит: удалять его нужно у собственного parentNode.
ВозвратУдалённый узел. Его parentNode становится null, ownerDocument сохраняется.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа; NOT_FOUND_ERR, если опорный/удаляемый ребёнок не принадлежит указанному родителю. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Живые спискиchildNodes и getElementsByTagName меняются во время удаления. Увеличение индекса после removeChild часто пропускает следующего ребёнка. Используйте while(firstChild), проход с конца или заранее сохранённый список объектов.
Удаление — разрыв связи
  1. order → item; переменная item держит объект
  2. order.removeChild(item)
  3. order пуст; item по-прежнему доступен в переменной
Убрать товар из заказа и перенести его в архивСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<shop><order><item>Book</item></order><archive/></shop>');
$shop = $document->documentElement;
$order = $shop->firstElementChild;
$archive = $order->nextElementSibling;
$removed = $order->removeChild($order->firstChild);
echo 'Удалённый товар: ', $removed->textContent, "\n";
echo 'После удаления: ', $document->saveXml($shop), "\n";
$archive->appendChild($removed);
echo 'После архивирования: ', $document->saveXml($shop), "\n";
Ожидаемый результат
Удалённый товар: Book
После удаления: <shop><order/><archive/></shop>
После архивирования: <shop><order/><archive><item>Book</item></archive></shop>

RemoveChild разорвал связь order→item и вернул живой объект в removed. Текст ещё доступен; второй вызов appendChild использует возвращённый узел в архиве того же документа. Удаление из дерева не уничтожило данные. Так различаются удалённая связь, оставшийся PHP-объект и новая связь archive→item.

Очистить корзину, сохранив названия всех снятых позицийСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<cart><item>A</item><item>B</item><item>C</item></cart>');
$cart = $document->documentElement;
$removedNames = [];
while ($cart->firstChild !== null) {
    $removed = $cart->removeChild($cart->firstChild);
    $removedNames[] = $removed->textContent;
}
echo 'Сняты позиции: ', implode(', ', $removedNames), "\n";
echo 'Корзина: ', $document->saveXml($cart), "\n";
Ожидаемый результат
Сняты позиции: A, B, C
Корзина: <cart/>

While каждый раз заново читает первого оставшегося ребёнка: сначалаA, затемB, затемC. Присваивание [] добавляет его название в массив; implode соединяет названия для журнала. После C firstChild равен null и цикл заканчивается. При увеличении индекса живой коллекции следующая позиция могла бы сдвинуться на уже пройденный индекс.

Связанные операции: Dom\Node::appendChild, Dom\Node::replaceChild.

Dom\Node::cloneNodeПолучить независимую заготовку карточки товара: исходный узел остаётся на месте, а копию можно изменить и затем вставить.

PHP 8.4+ · dom

Dom\Node::cloneNode(bool $deep = false): Dom\Node

Получить независимую заготовку карточки товара: исходный узел остаётся на месте, а копию можно изменить и затем вставить.

Dom\XMLDocument::createFromString читает известную XML-строку и возвращает документ в document. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. CloneNode возвращает отдельный узел. Первый пример прямо сопоставляет XML двух копий: отсутствие name видно без подсчёта детей. Второй применяет глубокую копию как заготовку товараB и оставляет товарA прежним.

Аргумент / поведениеПолный контракт
$deep = falsefalse копирует сам узел; true рекурсивно копирует потомков. Атрибуты элемента копируются даже при false. У копии нет parentNode.
ВозвратНовый Node. Копия обычного узла принадлежит прежнему ownerDocument; это не импорт в другой документ.
Идентификаторы и ограниченияid и другие атрибуты тоже копируются: приложение должно выдать новый id, если он должен быть уникален. Копируются DOM-данные, а не произвольная история PHP-объекта. Документ/поддерево из другого документа можно перенести через adoptNode или копировать через importNode.
Два товара из одного шаблона
  1. Исходник: item id=A → name=Book
  2. cloneNode(true): второй item id=A → отдельный name=Book
  3. Изменить id=B и текст только у копии; затем appendChild
Увидеть, что скопировано без детей и вместе с нимиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<item sku="A"><name>Book</name></item>');
$item = $document->documentElement;
$shallow = $item->cloneNode();
$deep = $item->cloneNode(true);
echo 'Без детей: ', $document->saveXml($shallow), "\n";
echo 'С детьми: ', $document->saveXml($deep), "\n";
echo 'Исходник: ', $document->saveXml($item), "\n";
Ожидаемый результат
Без детей: <item sku="A"/>
С детьми: <item sku="A"><name>Book</name></item>
Исходник: <item sku="A"><name>Book</name></item>

Без аргумента deep=false: атрибут sku скопирован, ребёнок name и его Text Book —нет. True копирует всё поддерево. Оба return —отдельные узлы без родителя; сохранение их строкой не вставляет копии в исходный документ. Чтобы создать второй товар в каталоге, нужно отдельно изменить id и вызвать appendChild, как ниже.

Создать второй товар и не изменить первыйСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item id="A"><name>Book</name></item></catalog>');
$catalog = $document->documentElement;
$copy = $catalog->firstElementChild->cloneNode(true);
$copy->setAttribute('id', 'B');
$copy->firstElementChild->textContent = 'Notebook';
$catalog->appendChild($copy);
foreach ($catalog->childNodes as $item) {
    echo $item->getAttribute('id'), ':', $item->textContent, "
";
}
Ожидаемый результат
A:Book
B:Notebook

Здесь копия глубока относительно DOM-дерева. Это другой механизм, чем обычный PHP clone с вложенными пользовательскими объектами; правила свойств PHP-класса не подменяют правила DOM.

Связанные операции: Dom\Node::isSameNode, Dom\Node::isEqualNode, Dom\Document::importNode.

Dom\Node::normalizeПосле нескольких добавлений текста собрать соседние текстовые узлы в один, чтобы обход документа не зависел от истории его редактирования.

PHP 8.4+ · dom

Dom\Node::normalize(): void

После нескольких добавлений текста собрать соседние текстовые узлы в один, чтобы обход документа не зависел от истории его редактирования.

Dom\XMLDocument::createFromString читает известную XML-строку и возвращает документ в document. Свойство documentElement даёт корневой элемент. SaveXml(узел) возвращает XML-строку именно этого поддерева для echo; файл на диске этим вызовом не изменяется. Иногда видимая строка AB построена двумя соседними Text. Normalize объединяет их на месте, не меняя саму строку. Первый опыт специально показывает одновременно число Text и их общее содержимое —это различие структуры при одинаковом тексте. Затем видна граница вложенного strong.

Аргумент / поведениеПолный контракт
АргументыНет. Обрабатывается текущий узел и его поддерево.
ИзменениеСоседние Text объединяются, пустые текстовые узлы удаляются. Граница элемента остаётся границей: текст по разные стороны вложенного элемента не склеивается в один узел.
Возвратvoid: полезный результат находится в изменённом дереве. Не присваивайте результат обратно переменной узла.
ГраницыЭто не проверка XML по схеме, не санитайзер HTML, не удаление пробелов и не Unicode-нормализация NFC. Пробел с текстом остаётся значимым содержимым.
Сборка текстовых фрагментов
  1. label → Text("A") → Text("") → Text("B")
  2. label.normalize()
  3. label → Text("AB"); видимый текст прежний, узлов меньше
Склеить два добавленных фрагментаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label/>');
$label = $document->documentElement;
$label->appendChild($document->createTextNode('A'));
$label->appendChild($document->createTextNode('B'));
echo $label->childNodes->length, ':', $label->textContent, "
";
$label->normalize();
echo $label->childNodes->length, ':', $label->textContent, "
";
Ожидаемый результат
2:AB
1:AB

Один и тот же видимый текст может состоять из разного числа узлов. Нормализация убирает эту зависимость для соседних текстовых фрагментов.

Собрать текст, сохранив выделение в описанииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<description/>');
$description = $document->documentElement;
$description->appendChild($document->createTextNode('Buy '));
$strong = $description->appendChild($document->createElement('strong'));
$strong->appendChild($document->createTextNode('A'));
$strong->appendChild($document->createTextNode('B'));
$description->appendChild($document->createTextNode(' now'));
$description->normalize();
echo $document->saveXml($description), "\n";
echo 'Текст посетителю: ', $description->textContent, "\n";
echo 'Узлов внутри strong: ', $strong->childNodes->length, "\n";
Ожидаемый результат
<description>Buy <strong>AB</strong> now</description>
Текст посетителю: Buy AB now
Узлов внутри strong: 1

Первый appendChild элемента возвращает strong; следующие вызовы создают два его соседних Text. Normalize возвращает void —результат нужно читать в дереве, не присваивать обратно description. Внутри strong остался один Text AB; элементstrong и пробелы Buy/now сохранены. Число1 здесь измеряет конкретный эффект склейки, а вся XML-строка показывает неизменённую разметку.

Связанные операции: Dom\Node::hasChildNodes, Dom\Text::splitText.

DOMNode::hasChildNodesУзнать, есть ли у блока дети, прежде чем выбирать режим обработки. Текст тоже считается ребёнком.

PHP 5+ · dom

DOMNode::hasChildNodes(): bool

Узнать, есть ли у блока дети, прежде чем выбирать режим обработки. Текст тоже считается ребёнком.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Задача — прочитать содержимое только после проверки, что ребёнок существует. Булев результат управляет if. Текст Book сам является ребёнком name; атрибут имени ребёнком не стал бы. Следующий пример уточняет отдельное правило: полезно ли содержимое заметки человеку.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; bool, true при наличии хотя бы одного дочернего узла любого типа.
Что именно проверяетсяText, комментарий и пробельный Text учитываются; атрибут не ребёнок. Метод не отвечает на вопрос «есть ли вложенные элементы товара». Для него нужны firstElementChild или childElementCount.
Изменение / ошибкиНе изменяет дерево. Проверка пустого корректного узла возвращает false, исключение для пустоты не требуется.
У элемента есть текстовый ребёнок
  1. name → Text("Book")
  2. hasChildNodes() = true
  3. firstElementChild = null: вложенного элемента нет
Прочитать текстового ребёнка и отдельно проверить вложенный элементСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<name>Book</name>');
$name = $document->documentElement;
if ($name->hasChildNodes()) {
    echo 'Первый ребёнок: ', $name->firstChild->nodeName, "\n";
    echo 'Его текст: ', $name->firstChild->textContent, "\n";
}
if ($name->firstElementChild === null) {
    echo "Вложенного элемента нет\n";
}
Ожидаемый результат
Первый ребёнок: #text
Его текст: Book
Вложенного элемента нет

HasChildNodes возвращает true, поэтому if разрешает прочитать firstChild. Это Text с именем #text и содержимым Book. FirstElementChild пропускает текст и возвращает null: внутри name нет второго тега. Скобки () вызывают метод без аргументов; стрелка -> читает свойство конкретного объекта. Наличие ребёнка ещё не обещает вложенный элемент.

Подготовить заметки менеджеру, различая пустоту и пробелыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<notes><note/><note> </note><note>Call buyer</note></notes>');
foreach ($document->documentElement->childNodes as $note) {
    if (!$note->hasChildNodes()) {
        echo "Нет узлов: запросить заметку\n";
        continue;
    }
    $message = trim($note->textContent);
    echo $message === '' ? "Только пробелы: запросить заметку\n" : "Менеджеру: $message\n";
}
Ожидаемый результат
Нет узлов: запросить заметку
Только пробелы: запросить заметку
Менеджеру: Call buyer

Первый note сразу попадает в ветку отсутствия детей; continue переходит к следующему. У второго есть Text с пробелом, но trim даёт пустую строку. Третий даёт настоящее сообщение. Trim допустим здесь по правилу поля заметки; пробелы в произвольном XML нельзя объявить лишними для всех элементов.

Связанные операции: DOMNode::normalize.

DOMNode::hasAttributesПонять, есть ли атрибуты у элемента, например сохранены ли какие-либо настройки фильтра.

PHP 5+ · dom

DOMNode::hasAttributes(): bool

Понять, есть ли атрибуты у элемента, например сохранены ли какие-либо настройки фильтра.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. У filter могут быть сохранённые настройки. HasAttributes без аргументов отвечает только «есть ли хотя бы один атрибут». Первый пример показывает сами сохранённые данные; следующий читает конкретный page и выбирает значение по умолчанию.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; bool. Для элемента true означает хотя бы один атрибут. На узлах других типов атрибутов нет.
Граница вопросаНе проверяет конкретное имя и непустое значение. Атрибут enabled="" всё равно существует. Для конкретного имени используйте DOMElement::hasAttribute; пространство имён проверяйте через hasAttributeNS.
Современный APIУ Dom\Node этого метода нет: он принадлежит Dom\Element. Декларации пространств имён в старом и современном DOM представлены по-разному; не используйте количество атрибутов для подсчёта xmlns.
Наличие не равно значению
  1. filter enabled=""
  2. hasAttributes() = true
  3. getAttribute("enabled") = пустая строка
Отличить заданную настройку от её пустого значенияСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<filter enabled=""/>');
$filter = $document->documentElement;
if ($filter->hasAttributes()) {
    echo 'Сохранён фильтр: ', $document->saveXML($filter), "\n";
}
echo 'Значение enabled: [', $filter->getAttribute('enabled'), "]\n";
Ожидаемый результат
Сохранён фильтр: <filter enabled=""/>
Значение enabled: []

HasAttributes нашёл хотя бы один атрибут и дал true для if. GetAttribute вернул пустую строку; квадратные скобки добавлены нами для видимости её границ. Существование enabled не объясняет, включён ли фильтр: формат настроек должен отдельно определить смысл пустого значения.

Прочитать номер страницы из разных настроек фильтраСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<filters><filter/><filter page="2"/><filter sort="price"/></filters>');
foreach ($document->documentElement->childNodes as $filter) {
    echo $filter->hasAttributes() ? 'Сохранённые настройки; ' : 'Настройки по умолчанию; ';
    $page = $filter->hasAttribute('page') ? $filter->getAttribute('page') : '1';
    echo 'страница ', $page, "\n";
}
Ожидаемый результат
Настройки по умолчанию; страница 1
Сохранённые настройки; страница 2
Сохранённые настройки; страница 1

Первый вызов отвечает о любых атрибутах. Второй спрашивает конкретно page: sort не задаёт страницу, поэтому используется 1. Здесь XML с известными значениями; номер из внешнего файла нужно дополнительно проверить как допустимое целое перед запросом к базе.

Связанные операции: DOMElement::hasAttribute, Dom\Element::hasAttributes.

DOMNode::containsПроверить, что найденный узел действительно находится внутри разрешённого блока документа, а не в другой части дерева.

PHP 8.3+ · dom

DOMNode::contains(DOMNode|DOMNameSpaceNode|null $other): bool

Проверить, что найденный узел действительно находится внутри разрешённого блока документа, а не в другой части дерева.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Выделен item; нужно отличить его цену от одноимённой цены в служебной ветке. Вызов item->contains(candidate) читает связь дерева, ничего не перемещает. True разрешает действие с кандидатом. Сначала видны три простейших случая, затем изменение цены только в нужной области.

Аргумент / поведениеПолный контракт
$otherПроверяемый узел или null. Допускается также DOMNameSpaceNode, возвращаемый некоторыми XPath-запросами.
Возвратbool: true для самого узла и его потомков на любой глубине; false для null, соседа, предка или узла другого дерева.
Версия / ограниченияСтарый DOMNode получил метод в PHP 8.3. Проверка принадлежности дереву не является проверкой прав пользователя или безопасностью HTML.
Граница редактирования
  1. catalog → item → price; рядом settings
  2. item.contains(price) = true
  3. item.contains(settings) = false; item.contains(item) = true
Показать, какие записи входят в выбранный блокСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<item><price>100</price></item>');
$item = $document->documentElement;
foreach ([$item, $item->firstChild, null] as $candidate) {
    if ($item->contains($candidate)) {
        echo 'В области item: ', $candidate->nodeName, "\n";
    } else {
        echo "Кандидат отсутствует или вне области item\n";
    }
}
Ожидаемый результат
В области item: item
В области item: price
Кандидат отсутствует или вне области item

Массив содержит три разных аргумента: сам item, его price, затем null. Contains возвращает true, true, false. Поэтому первые две ветки читают имя объекта, а последняя не пытается читать свойство null. Contains включает сам контейнер; для строгого потомка нужно также исключить isSameNode.

Не изменять служебную настройку вместо цены товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root><item><price>100</price></item><settings><price>999</price></settings></root>');
$item = $document->documentElement->firstElementChild;
foreach ($document->getElementsByTagName('price') as $price) {
    if ($item->contains($price)) {
        $price->textContent = '120';
    }
}
foreach ($document->getElementsByTagName('price') as $price) {
    echo $price->textContent, "
";
}
Ожидаемый результат
120
999

Имя price встречается в нескольких ветках. contains задаёт структурную границу; в реальном редакторе права доступа проверяются ещё до этой операции.

Связанные операции: DOMNode::isSameNode, DOMNode::getRootNode.

DOMNode::getRootNodeОтличить узел, включённый в документ, от отдельно подготовленной ветки, которой ещё предстоит вставка.

PHP 8.3+ · dom

DOMNode::getRootNode(?array $options = null): DOMNode

Отличить узел, включённый в документ, от отдельно подготовленной ветки, которой ещё предстоит вставка.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Создать узел документом ещё не значит вставить его в документ. Метод без аргументов возвращает самый верхний узел именно текущей цепочки родителей. Первое действие ниже читает имя этого корня, следующее использует различие при подготовке правки.

Аргумент / поведениеПолный контракт
$optionsНеобязательный ?array со значением null. В PHP DOM этот параметр сейчас не влияет на результат; браузерные варианты с Shadow DOM сюда переносить не нужно.
ВозвратКорень текущего дерева Node: подъём по parentNode до отсутствующего родителя. Для подключённого элемента это документ; для отсоединённого поддерева — верхний элемент, а не ownerDocument.
Изменение / версияТолько чтение. В старом DOM метод появился в PHP 8.3.
Две связи у отсоединённой ветки
  1. item создан документом: ownerDocument → document
  2. item не вставлен: parentNode = null
  3. item.getRootNode() → item; после вставки → document
Увидеть корень заготовки до и после включения в каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$item = $document->createElement('item');
$item->textContent = 'Book';
$rootBefore = $item->getRootNode();
echo 'Корень заготовки: ', $rootBefore->nodeName, "\n";
echo 'Каталог до: ', $document->saveXml($document->documentElement), "\n";
$document->documentElement->appendChild($item);
$rootAfter = $item->getRootNode();
echo 'Корень после: ', $rootAfter->nodeName, "\n";
echo 'Каталог после: ', $document->saveXml($rootAfter->documentElement), "\n";
Ожидаемый результат
Корень заготовки: item
Каталог до: <catalog/>
Корень после: #document
Каталог после: <catalog><item>Book</item></catalog>

GetRootNode вернул объект в rootBefore: отдельный item, у которого нет родителя. После appendChild путь item→catalog→document закончился документом в rootAfter. Его documentElement снова даёт каталог для вывода. OwnerDocument у item всё это время был document; создавший документ и текущий корень — разные связи.

Проверить отделённую ветку перед возвратом в документСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item><name>Book</name></item></catalog>');
$catalog = $document->documentElement;
$item = $catalog->removeChild($catalog->firstChild);
$name = $item->firstChild;
if (!$name->getRootNode()->isSameNode($document)) {
    echo 'Ещё не в каталоге: ', $name->textContent, "\n";
    $name->textContent = 'Notebook';
    $catalog->appendChild($item);
}
echo $document->saveXml($catalog), "\n";
Ожидаемый результат
Ещё не в каталоге: Book
<catalog><item><name>Notebook</name></item></catalog>

После removeChild корень name — item. Цепочка getRootNode()->isSameNode сначала получает этот корень, затем сравнивает его с document; ! обращает false в true. Правим отсоединённую ветку и вставляем целиком. Это изменение дерева в памяти, не публикация и не запись на диск.

Связанные операции: DOMNode::contains, DOMNode::removeChild.

DOMNode::isSameNodeПроверить идентичность узла: два результата поиска указывают на одну запись дерева или на две похожие записи?

PHP 5+ · dom

DOMNode::isSameNode(DOMNode $otherNode): bool

Проверить идентичность узла: два результата поиска указывают на одну запись дерева или на две похожие записи?

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. При двух поисках можно получить две переменные для одной записи. IsSameNode принимает второй узел и возвращает bool; первая правка показывает практическое последствие true. Копия даёт false и меняется отдельно. Во втором примере идентичность исключает сам контейнер из строгих потомков.

Аргумент / поведениеПолный контракт
$otherNodeДругой DOMNode; null не соответствует типу аргумента.
Возвратbool, true только для того же базового DOM-узла. Имя, текст и атрибуты сами по себе идентичность не определяют.
ВыборДля сравнения содержимого существует isEqualNode. Для PHP-переменных современный runtime также сохраняет идентичность обёртки, но метод явно выражает DOM-вопрос. Не сравнивайте идентичность по сериализованному XML.
Один узел найден двумя путями
  1. root.firstChild → item-A
  2. getElementsByTagName("item")[0] → item-A
  3. isSameNode = true; cloneNode → другой узел
Узнать общую запись у двух результатов поискаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>Book</item></catalog>');
$first = $document->documentElement->firstChild;
$found = $document->getElementsByTagName('item')->item(0);
if ($first->isSameNode($found)) {
    $found->textContent = 'Notebook';
}
echo 'Через first: ', $first->textContent, "\n";
$copy = $first->cloneNode(true);
if (!$first->isSameNode($copy)) {
    $copy->textContent = 'Copy';
}
echo 'Копия: ', $copy->textContent, "\n";
echo 'Оригинал: ', $first->textContent, "\n";
Ожидаемый результат
Через first: Notebook
Копия: Copy
Оригинал: Notebook

Item(0) берёт первый объект списка поиска, индексы начинаются с нуля. First и found указывают на один item: правка через found видна через first. CloneNode(true) возвращает другой узел с теми же начальными данными. Его отдельная правка не изменила оригинал. Метод сравнивает идентичность узла, не его имя или текст.

Не включать сам контейнер в список его строгих потомковСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<group><item>A</item></group>');
$group = $document->documentElement;
$candidates = [$group, $group->firstChild];
foreach ($candidates as $candidate) {
    if ($group->contains($candidate) && !$group->isSameNode($candidate)) {
        echo $candidate->nodeName, "
";
    }
}
Ожидаемый результат
item

contains включает сам group. Отрицание ! перед isSameNode исключает только контейнер, а не похожие элементы.

Связанные операции: DOMNode::isEqualNode, DOMNode::contains.

DOMNode::isEqualNodeСравнить содержимое двух XML-блоков перед заменой: если структура не изменилась, можно пропустить запись новой версии.

PHP 8.3+ · dom

DOMNode::isEqualNode(?DOMNode $otherNode): bool

Сравнить содержимое двух XML-блоков перед заменой: если структура не изменилась, можно пропустить запись новой версии.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Перед заменой XML-настройки полезно понять, изменились ли данные. IsEqualNode возвращает bool для содержимого; клон может быть равным, не будучи тем же объектом. Показываем обе версии XML, затем применяем сравнение к двум последовательным настройкам валюты.

Аргумент / поведениеПолный контракт
$otherNodeДругой узел или null. null даёт false.
Возвратbool. Сравниваются характеристики соответствующих типов узлов, атрибуты и последовательность дочерних узлов рекурсивно. Разное положение в документе не мешает равенству.
Что важноПорядок атрибутов не является порядком детей; текстовые пробелы и разные типы узлов влияют на равенство. Это не бизнес-сравнение цен 1 и 1.00 и не проверка эквивалентности произвольных XML-схем.
Стоимость / версияНужно сравнивать поддеревья, а не только адрес объекта; объём работы зависит от размера до первого отличия. В старом DOMNode метод доступен с PHP 8.3.
Похожий не значит тот же
  1. item-A → name=Book
  2. clone item-B → name=Book: equal true, same false
  3. name у B меняется: equal false
Показать содержимое равных и изменённых копийСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<item sku="A">Book</item>');
$item = $document->documentElement;
$copy = $item->cloneNode(true);
if ($item->isEqualNode($copy)) {
    echo 'Одинаковое содержимое: ', $document->saveXml($copy), "\n";
}
$copy->textContent = 'Notebook';
if (!$item->isEqualNode($copy)) {
    echo 'Было: ', $document->saveXml($item), "\n";
    echo 'Стало: ', $document->saveXml($copy), "\n";
}
Ожидаемый результат
Одинаковое содержимое: <item sku="A">Book</item>
Было: <item sku="A">Book</item>
Стало: <item sku="A">Notebook</item>

Первый true означает равенство структуры и значений, хотя cloneNode создал отдельный объект. После textContent=Notebook результат false позволяет показать различие. IsEqualNode не приводит Book и Notebook к общему виду и не игнорирует пробелы по бизнес-правилам.

Пропустить одинаковую настройку и принять изменённую валютуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<settings><currency code="RUB"/></settings>');
$settings = $document->documentElement;
foreach (['RUB', 'EUR'] as $code) {
    $current = $settings->firstChild;
    $candidate = $document->createElement('currency');
    $candidate->setAttribute('code', $code);
    if ($current->isEqualNode($candidate)) {
        echo 'Без изменения: ', $code, "\n";
        continue;
    }
    $settings->replaceChild($candidate, $current);
    echo 'Сохранена настройка: ', $document->saveXml($settings), "\n";
}
Ожидаемый результат
Без изменения: RUB
Сохранена настройка: <settings><currency code="EUR"/></settings>

В первой итерации кандидат RUB совпадает с текущим узлом; continue пропускает замену. Во второй отличается атрибут code, поэтому replaceChild меняет дерево. Обе ветки видны в одном запуске. Для большого документа сравнение поддеревьев может быть дороже проверки нескольких значимых полей; сначала определите требуемое правило равенства.

Связанные операции: DOMNode::isSameNode, DOMNode::normalize, DOMNode::C14N.

DOMNode::compareDocumentPositionУзнать порядок двух найденных узлов и отдельно проверить вложенность; это полезно для упорядочивания результатов из разных поисков.

PHP 8.4+ · dom

DOMNode::compareDocumentPosition(DOMNode $other): int

Узнать порядок двух найденных узлов и отдельно проверить вложенность; это полезно для упорядочивания результатов из разных поисков.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Импортёр нашёл задачи разными запросами; нужно восстановить их положение в одном дереве. Метод возвращает целое с несколькими флагами. Сначала учимся читать одну маску20, затем проверяем связность перед использованием порядка. Таблица сохраняет значения и направление всех флагов.

Аргумент / поведениеПолный контракт
$otherУзел, чьё положение сравнивается относительно текущего. Аргумент обязателен.
Возвратint — битовая маска, а не номер позиции. Одинаковый узел даёт 0; PRECEDING=2, FOLLOWING=4; CONTAINS=8 означает, что other — предок текущего; CONTAINED_BY=16, что other — потомок.
Несвязанные узлыDISCONNECTED=1 и IMPLEMENTATION_SPECIFIC=32 предупреждают об отсутствии переносимого общего порядка. В маске может быть несколько флагов; проверяйте ($position & FLAG) !== 0, а не равенство одному флагу.
ВерсияPHP 8.4+ для обоих семейств. Для вопроса «внутри ли узел?» проще contains.
Направление сравнения
  1. root.compareDocumentPosition(child)
  2. other=child следует после root и находится в нём
  3. FOLLOWING 4 | CONTAINED_BY 16 = 20
Прочитать маску как несколько отношений между узламиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<queue><task>A</task><task>B</task></queue>');
$queue = $document->documentElement;
$first = $queue->firstChild;
$last = $queue->lastChild;
$position = $queue->compareDocumentPosition($first);
echo 'Маска queue к A: ', $position, "\n";
if (($position & DOMNode::DOCUMENT_POSITION_CONTAINED_BY) !== 0) {
    echo 'Внутри queue: ', $first->textContent, "\n";
}
if (($last->compareDocumentPosition($first) & DOMNode::DOCUMENT_POSITION_PRECEDING) !== 0) {
    echo 'Раньше B выполнить: ', $first->textContent, "\n";
}
Ожидаемый результат
Маска queue к A: 20
Внутри queue: A
Раньше B выполнить: A

CompareDocumentPosition у queue получает first=A. Число20 складывает флаги4 и16: другой узел следует за контейнером и вложен в него. Побитовое & оставляет интересующий бит; !==0 проверяет его наличие. Во втором вызове точка отсчёта B, другой узел A; PRECEDING означает «A раньше B». Это порядок дерева, не часы выполнения задания.

Выстроить две найденные задачи и отложить несвязанную заготовкуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<queue><task>A</task><task>B</task></queue>');
$first = $document->documentElement->firstChild;
$last = $document->documentElement->lastChild;
$draft = $document->createElement('task');
$draft->textContent = 'C';
foreach ([$last, $draft] as $other) {
    $position = $first->compareDocumentPosition($other);
    if (($position & DOMNode::DOCUMENT_POSITION_DISCONNECTED) !== 0) {
        echo 'Вне очереди, нужен приоритет: ', $other->textContent, "\n";
    } elseif (($position & DOMNode::DOCUMENT_POSITION_FOLLOWING) !== 0) {
        echo 'Порядок очереди: ', $first->textContent, ' -> ', $other->textContent, "\n";
    }
}
Ожидаемый результат
Порядок очереди: A -> B
Вне очереди, нужен приоритет: C

Сначала проверяется DISCONNECTED: заготовка C ещё не вставлена и не имеет переносимого места относительно A. Для B деревья общие, FOLLOWING позволяет вывести A→B. Сравнивать всю маску с4 неправильно: вместе с порядком может присутствовать флаг вложенности. Бизнес-приоритет несвязанных задач задают отдельными данными.

Связанные операции: DOMNode::contains, DOMNode::isSameNode.

DOMNode::getLineNoПоказать автору XML строку проблемной записи вместо сообщения «ошибка где-то в файле».

PHP 5.3+ · dom

DOMNode::getLineNo(): int

Показать автору XML строку проблемной записи вместо сообщения «ошибка где-то в файле».

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. XML из примера содержит настоящие переводы строк. GetLineNo без аргументов возвращает исходный номер, который echo выводит или сообщение об ошибке присоединяет к названию поля. Сначала строка2 сопоставляется с новым узлом без координаты0; затем строка3 помогает найти отсутствующую цену.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int — номер строки исходного разбора для узла, когда он доступен. Узел, созданный программно, обычно даёт 0.
ТочностьЭто метаданные загрузки, а не номер строки будущего saveXML. После переносов/редактирования номер не пересчитывается под новый вывод. Для очень больших XML поведение старых libxml зависело от хранения номеров; LIBXML_BIGLINES используется при разборе там, где поддерживается.
Версия / данные журналаСтарый метод доступен с PHP 5.3. Номер строки безопаснее полного XML в логе, который может содержать адреса и токены.
Диагностика импорта
  1. Исходный XML: item на строке 2
  2. Проверка price обнаружила пустое значение
  3. getLineNo() → 2; пользователю возвращается координата, не весь документ
Получить исходную строку элементаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog>
<item>Book</item>
</catalog>');
$item = $document->getElementsByTagName('item')->item(0);
echo $item->getLineNo(), "
";
echo $document->createElement('item')->getLineNo(), "
";
Ожидаемый результат
2
0

Ноль у новой заготовки означает отсутствие исходной строки; это не нулевая строка XML-файла.

Сообщить координату записи с отсутствующей ценойСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog>
<item price="100"/>
<item/>
</catalog>');
foreach ($document->getElementsByTagName('item') as $item) {
    if (!$item->hasAttribute('price')) {
        echo 'Missing price at line ', $item->getLineNo(), "
";
    }
}
Ожидаемый результат
Missing price at line 3

hasAttribute здесь проверяет только наличие. Следующий этап импорта обязан проверить формат и диапазон цены.

Связанные операции: DOMNode::getNodePath.

DOMNode::getNodePathПолучить путь к конкретной записи для диагностики импорта или повторного выбора в неизменённом документе.

PHP 5.2+ · dom

DOMNode::getNodePath(): ?string

Получить путь к конкретной записи для диагностики импорта или повторного выбора в неизменённом документе.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Если импортёр сообщает только «товар неверен», автору трудно найти запись. GetNodePath возвращает её текущую координату. Простой вывод показывает второй item; затем эта же строка используется как запрос и сопоставляется с изменением дерева. Индекс XPath2 начинается с1; NodeList item(0) начинается с0.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; ?string, null при ошибке получения пути с XPath-подобным местоположением узла.
Не постоянный IDИндекс зависит от соседей, например item[2]. Перестановка или вставка узлов меняет путь. Храните бизнес-ID в атрибуте, если ссылка должна переживать редактирование.
Пространства имёнПрефикс в пути необходимо привязать к URI в XPath-контексте; путь не заменяет registerNamespace. У документа без префиксов учебные пути можно выполнить напрямую.
ВерсияСтарый API PHP 5.2+. Путь может раскрывать структуру документа: выдавайте только нужную диагностику.
Координата второго товара
  1. catalog → item[1], item[2]
  2. item[2].getNodePath()
  3. /catalog/item[2] относится к текущему порядку, а не навсегда к товару
Увидеть путь второго элементаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item><item>B</item></catalog>');
echo $document->documentElement->lastChild->getNodePath(), "
";
Ожидаемый результат
/catalog/item[2]

Квадратные скобки здесь относятся к XPath и нумеруются с 1, в отличие от индексов NodeList с 0.

Прочитать товар по пути и увидеть изменение координатыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item><item>B</item></catalog>');
$item = $document->documentElement->lastChild;
$path = $item->getNodePath();
$query = new DOMXPath($document);
$found = $query->query($path)->item(0);
echo 'По пути ', $path, ': ', $found->textContent, "\n";
$document->documentElement->removeChild($document->documentElement->firstChild);
echo 'Тот же товар ', $item->textContent, ' теперь: ', $item->getNodePath(), "\n";
Ожидаемый результат
По пути /catalog/item[2]: B
Тот же товар B теперь: /catalog/item

GetNodePath возвращает строку, присвоенную path. XPath —объект для запросов по структуре; query(path) возвращает список, item(0) берёт его первый результат. Здесь путь получен из того же неизменённого документа, поэтому B найден. Удаление A меняет координату B; старый путь не является устойчивым ID. Для произвольного запроса отдельно обрабатывайте ошибку query и пустой результат.

Связанные операции: DOMXPath::query, DOMXPath::registerNamespace, DOMNode::getLineNo.

DOMNode::lookupNamespaceURIПо короткому префиксу выяснить, к какому XML-пространству имён он относится именно в этой точке документа.

PHP 5+ · dom

DOMNode::lookupNamespaceURI(?string $prefix): ?string

По короткому префиксу выяснить, к какому XML-пространству имён он относится именно в этой точке документа.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Префикс —локальное сокращение URI: один и тот же p может означать разные словари в соседних ветках. LookupNamespaceURI вызывается в нужном контексте и возвращает строку либо null. Сначала читаем привязку, затем применяем её к разбору акционного значения.

Аргумент / поведениеПолный контракт
$prefixСтрока префикса; null запрашивает пространство имён по умолчанию. Ищется привязка в контексте узла и его предков, с учётом переопределений.
Возврат?string: URI найденной привязки либо null при её отсутствии. URI — идентификатор; метод не скачивает его по сети.
ГраницаПрефикс не является постоянным именем схемы: два поставщика могут назвать один URI по-разному. Для сравнения смысла используйте namespaceURI + localName, а XPath регистрируйте с собственным префиксом.
Префикс имеет локальное значение
  1. root: xmlns:p=urn:prices
  2. вложенный item: xmlns:p=urn:promo
  3. lookupNamespaceURI("p") у item → urn:promo
Прочитать привязку префикса и обработать неизвестное имяСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns="urn:catalog" xmlns:p="urn:prices"/>');
$root = $document->documentElement;
echo 'Префикс p означает: ', $root->lookupNamespaceURI('p'), "\n";
echo 'Без префикса: ', $root->lookupNamespaceURI(null), "\n";
$uri = $root->lookupNamespaceURI('unknown');
if ($uri === null) {
    echo "unknown не объявлен: запросить описание формата\n";
}
Ожидаемый результат
Префикс p означает: urn:prices
Без префикса: urn:catalog
unknown не объявлен: запросить описание формата

Строка p без двоеточия —аргумент поиска. Return urn:prices попадает прямо в echo. Аргумент null спрашивает default namespace; результат null у unknown означает отсутствие привязки. URI здесь служит именем формата, сетевого запроса не происходит.

Распознать акционное поле при локальной смене префиксаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root xmlns:p="urn:prices"><item xmlns:p="urn:promo"><p:value>10</p:value></item></root>');
$root = $document->documentElement;
$item = $root->firstElementChild;
$value = $item->firstElementChild;
echo 'p у root: ', $root->lookupNamespaceURI('p'), "\n";
echo 'p у item: ', $item->lookupNamespaceURI('p'), "\n";
if ($value->namespaceURI === 'urn:promo' && $value->localName === 'value') {
    echo 'Акционная величина: ', $value->textContent, "\n";
}
Ожидаемый результат
p у root: urn:prices
p у item: urn:promo
Акционная величина: 10

Item переобъявил p. Lookup показывает разные URI в двух контекстах; фактический элемент проверяется по паре namespaceURI и localName, поэтому10 не будет принято за обычную цену только по буквам p:value. Для бизнес-смысла суммы ещё нужны правила валюты и допустимого диапазона.

Связанные операции: DOMNode::lookupPrefix, DOMNode::isDefaultNamespace, DOMXPath::registerNamespace.

DOMNode::lookupPrefixНайти доступный префикс для известного URI, например для понятного диагностического сообщения о внешнем XML.

PHP 5+ · dom

DOMNode::lookupPrefix(string $namespace): ?string

Найти доступный префикс для известного URI, например для понятного диагностического сообщения о внешнем XML.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Когда URI уже известен, можно найти его сокращение для отчёта. Return —строка или null; ниже обе ветки дают понятную подпись. Второй пример объясняет, почему отсутствие именованного префикса совместимо с существующим пространством по умолчанию.

Аргумент / поведениеПолный контракт
$namespaceURI пространства имён строкой в старом API.
Возврат?string: подходящий префикс либо null, если префикса нет. Пространство по умолчанию может существовать без возвращаемого префикса. При нескольких привязках не делайте бизнес-логику зависимой от выбора конкретного варианта.
ГраницаНе создаёт декларацию xmlns и не регистрирует префикс в XPath. Для нового имени используйте createElementNS/setAttributeNS; для запроса registerNamespace.
Обратный поиск
  1. xmlns:p="urn:prices"
  2. lookupPrefix("urn:prices") → p
  3. xmlns="urn:catalog" не даёт обязательного именованного префикса
Сформировать понятную подпись поля по известному URIСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root xmlns:p="urn:prices"/>');
$root = $document->documentElement;
foreach (['urn:prices', 'urn:missing'] as $uri) {
    $prefix = $root->lookupPrefix($uri);
    $label = $prefix === null ? '{' . $uri . '}value' : $prefix . ':value';
    echo 'Поле отчёта: ', $label, "\n";
}
Ожидаемый результат
Поле отчёта: p:value
Поле отчёта: {urn:missing}value

LookupPrefix получил полный URI и вернул p либо null. Условие выбирает подпись: привычное p:value или явно показанный URI в фигурных скобках. Эти скобки —наша текстовая нотация отчёта, не создаваемый XML-тег и не XPath. Метод не создаёт нового префикса для отсутствующего пространства.

Показать имя элемента с пространством по умолчаниюСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns="urn:catalog"><item>Book</item></catalog>');
$item = $document->documentElement->firstElementChild;
$prefix = $item->lookupPrefix('urn:catalog');
if ($prefix === null) {
    echo "Именованного префикса нет\n";
}
echo 'Полное имя: {', $item->namespaceURI, '}', $item->localName, "\n";
echo 'Данные: ', $item->textContent, "\n";
Ожидаемый результат
Именованного префикса нет
Полное имя: {urn:catalog}item
Данные: Book

Null не доказывает, что item не принадлежит пространству: здесь действует default namespace. NamespaceURI и localName дают полное смысловое имя, а textContent —данные Book. Для XPath-запроса к этим элементам всё равно регистрируют собственный префикс, даже если исходный XML написал теги без него.

Связанные операции: DOMNode::lookupNamespaceURI, DOMXPath::registerNamespace.

DOMNode::isDefaultNamespaceПроверить, какой URI используется для непредварённых префиксом элементов в текущей области XML.

PHP 5+ · dom

DOMNode::isDefaultNamespace(string $namespace): bool

Проверить, какой URI используется для непредварённых префиксом элементов в текущей области XML.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Каталог использует пространство по умолчанию: его элементы написаны без префикса. IsDefaultNamespace возвращает решение для конкретного ожидаемого URI. После простого выбора формата разбираем частую ошибку: sku не наследует пространство своего элемента.

Аргумент / поведениеПолный контракт
$namespaceURI для сравнения строкой.
Возвратbool: совпадает ли аргумент с пространством имён по умолчанию в контексте узла.
Важное различиеНепрефиксные атрибуты XML не наследуют default namespace элемента. Проверка не отвечает, в каком пространстве находится произвольный атрибут, и не проверяет PHP namespace.
Пространство по умолчанию
  1. catalog xmlns="urn:catalog"
  2. item без префикса находится в urn:catalog
  3. атрибут sku без префикса находится без пространства имён
Выбрать правило обработки для непредварённого префиксом элементаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns="urn:catalog"><item>Book</item></catalog>');
$item = $document->documentElement->firstElementChild;
foreach (['urn:catalog', 'urn:other'] as $expected) {
    if ($item->isDefaultNamespace($expected)) {
        echo 'Подходит формат ', $expected, ': ', $item->textContent, "\n";
    } else {
        echo 'Не применять правила ', $expected, "\n";
    }
}
Ожидаемый результат
Подходит формат urn:catalog: Book
Не применять правила urn:other

IsDefaultNamespace сравнивает URI аргумента с текущей привязкой по умолчанию. True разрешает показать Book в ожидаемом формате; false отклоняет чужое правило. Проверка относится к default namespace, а не ко всем именам с префиксами. URI сравнивается как строка, без обращения к сети.

Прочитать sku без ошибочного пространства атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns="urn:catalog"><item sku="A">Book</item></catalog>');
$item = $document->documentElement->firstElementChild;
if ($item->isDefaultNamespace('urn:catalog')) {
    $sku = $item->getAttributeNS(null, 'sku');
    echo 'Товар ', $sku, ': ', $item->textContent, "\n";
}
if (!$item->hasAttributeNS('urn:catalog', 'sku')) {
    echo "Атрибута sku в urn:catalog нет\n";
}
Ожидаемый результат
Товар A: Book
Атрибута sku в urn:catalog нет

Default namespace относится к непредварённому префиксом элементу item, но не к атрибуту sku. Поэтому getAttributeNS получает null и возвращает A. HasAttributeNS с URI каталога показывает, почему такой ошибочный поиск ничего не найдёт. Legacy getAttributeNS при отсутствии даёт пустую строку, современный —null; hasAttributeNS отличает отсутствие от существующего пустого значения.

Связанные операции: DOMNode::lookupNamespaceURI, DOMElement::getAttributeNS.

DOMNode::isSupportedПрочитать старую декларацию поддержки DOM feature/version при сопровождении исторического кода.

PHP 5+ · dom

DOMNode::isSupported(string $feature, string $version): bool

Прочитать старую декларацию поддержки DOM feature/version при сопровождении исторического кода.

New DOMDocument создаёт document, loadXML читает указанную XML-строку; documentElement выбирает корневой элемент. Этот метод встречается в старом коде, проверяющем таблицу DOM feature/version. Сначала учимся правильно прочитать его историческое обещание. Затем решаем практический вопрос точнее: есть ли compareDocumentPosition и можно ли с его помощью вывести порядок задач.

Аргумент / поведениеПолный контракт
$featureНазвание DOM-функциональности, например Core или XML; это исторические feature-наборы, не имя PHP-метода.
$versionВерсия соответствующего feature-набора, например 1.0/2.0, а не версия PHP или libxml.
Возвратbool: поддерживается ли указанная пара по таблице старой реализации.
ОграниченияНе является надёжным современным feature detection для HTML5, CSS-селекторов, схем или безопасного разбора. Проверяйте конкретный class_exists/method_exists и нужную версию. В новом Dom\Node метода нет.
Старый и точный вопросы
  1. isSupported("XML","1.0"): старый набор DOM XML
  2. method_exists(...,"compareDocumentPosition"): конкретный PHP API
  3. Ни один из них не валидирует входной документ
Прочитать историческое обещание старого DOMСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root/>');
$node = $document->documentElement;
foreach (['XML', 'ImaginaryFeature'] as $feature) {
    if ($node->isSupported($feature, '1.0')) {
        echo 'Старый DOM заявляет: ', $feature, ' 1.0', "\n";
    } else {
        echo 'Обещания поддержки нет: ', $feature, "\n";
    }
}
Ожидаемый результат
Старый DOM заявляет: XML 1.0
Обещания поддержки нет: ImaginaryFeature

Feature и version —два аргумента старой таблицы возможностей. True для XML1.0 не является результатом проверки нашего XML-документа; false для выдуманного имени не означает поломку PHP. Для современного кода ниже проверяется наличие конкретного метода, который затем действительно используется.

Выбрать доступный способ проверки порядка двух задачСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<queue><task>A</task><task>B</task></queue>');
$first = $document->documentElement->firstChild;
$last = $document->documentElement->lastChild;
if (method_exists($first, 'compareDocumentPosition')) {
    $position = $first->compareDocumentPosition($last);
    if (($position & DOMNode::DOCUMENT_POSITION_FOLLOWING) !== 0) {
        echo 'Очередь: ', $first->textContent, ' -> ', $last->textContent, "\n";
    }
} else {
    echo "Нужен PHP 8.4+ для проверки позиции\n";
}
Ожидаемый результат
Очередь: A -> B

MethodExists спрашивает о конкретном API объекта. Только после true вызывается compareDocumentPosition; результат FOLLOWING используется для вывода реальных задач. На старом runtime исполнится понятный отказ. Это пример сопровождения совместимости; минимальную версию библиотеки также нужно зафиксировать в Composer, а не заменять множеством случайных проверок.

Связанные операции: DOMImplementation::hasFeature.

Dom\Node::hasChildNodesУзнать, есть ли у блока дети, прежде чем выбирать режим обработки. Текст тоже считается ребёнком.

PHP 8.4+ · dom

Dom\Node::hasChildNodes(): bool

Узнать, есть ли у блока дети, прежде чем выбирать режим обработки. Текст тоже считается ребёнком.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Задача — прочитать содержимое только после проверки, что ребёнок существует. Булев результат управляет if. Текст Book сам является ребёнком name; атрибут имени ребёнком не стал бы. Следующий пример уточняет отдельное правило: полезно ли содержимое заметки человеку.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; bool, true при наличии хотя бы одного дочернего узла любого типа.
Что именно проверяетсяText, комментарий и пробельный Text учитываются; атрибут не ребёнок. Метод не отвечает на вопрос «есть ли вложенные элементы товара». Для него нужны firstElementChild или childElementCount.
Изменение / ошибкиНе изменяет дерево. Проверка пустого корректного узла возвращает false, исключение для пустоты не требуется.
У элемента есть текстовый ребёнок
  1. name → Text("Book")
  2. hasChildNodes() = true
  3. firstElementChild = null: вложенного элемента нет
Прочитать текстового ребёнка и отдельно проверить вложенный элементСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<name>Book</name>');
$name = $document->documentElement;
if ($name->hasChildNodes()) {
    echo 'Первый ребёнок: ', $name->firstChild->nodeName, "\n";
    echo 'Его текст: ', $name->firstChild->textContent, "\n";
}
if ($name->firstElementChild === null) {
    echo "Вложенного элемента нет\n";
}
Ожидаемый результат
Первый ребёнок: #text
Его текст: Book
Вложенного элемента нет

HasChildNodes возвращает true, поэтому if разрешает прочитать firstChild. Это Text с именем #text и содержимым Book. FirstElementChild пропускает текст и возвращает null: внутри name нет второго тега. Скобки () вызывают метод без аргументов; стрелка -> читает свойство конкретного объекта. Наличие ребёнка ещё не обещает вложенный элемент.

Подготовить заметки менеджеру, различая пустоту и пробелыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<notes><note/><note> </note><note>Call buyer</note></notes>');
foreach ($document->documentElement->childNodes as $note) {
    if (!$note->hasChildNodes()) {
        echo "Нет узлов: запросить заметку\n";
        continue;
    }
    $message = trim($note->textContent);
    echo $message === '' ? "Только пробелы: запросить заметку\n" : "Менеджеру: $message\n";
}
Ожидаемый результат
Нет узлов: запросить заметку
Только пробелы: запросить заметку
Менеджеру: Call buyer

Первый note сразу попадает в ветку отсутствия детей; continue переходит к следующему. У второго есть Text с пробелом, но trim даёт пустую строку. Третий даёт настоящее сообщение. Trim допустим здесь по правилу поля заметки; пробелы в произвольном XML нельзя объявить лишними для всех элементов.

Связанные операции: Dom\Node::normalize.

Dom\Node::containsПроверить, что найденный узел действительно находится внутри разрешённого блока документа, а не в другой части дерева.

PHP 8.4+ · dom

Dom\Node::contains(?Dom\Node $other): bool

Проверить, что найденный узел действительно находится внутри разрешённого блока документа, а не в другой части дерева.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Выделен item; нужно отличить его цену от одноимённой цены в служебной ветке. Вызов item->contains(candidate) читает связь дерева, ничего не перемещает. True разрешает действие с кандидатом. Сначала видны три простейших случая, затем изменение цены только в нужной области.

Аргумент / поведениеПолный контракт
$otherПроверяемый узел или null. Современный Node.
Возвратbool: true для самого узла и его потомков на любой глубине; false для null, соседа, предка или узла другого дерева.
Версия / ограниченияСтарый DOMNode получил метод в PHP 8.3. Проверка принадлежности дереву не является проверкой прав пользователя или безопасностью HTML.
Граница редактирования
  1. catalog → item → price; рядом settings
  2. item.contains(price) = true
  3. item.contains(settings) = false; item.contains(item) = true
Показать, какие записи входят в выбранный блокСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<item><price>100</price></item>');
$item = $document->documentElement;
foreach ([$item, $item->firstChild, null] as $candidate) {
    if ($item->contains($candidate)) {
        echo 'В области item: ', $candidate->nodeName, "\n";
    } else {
        echo "Кандидат отсутствует или вне области item\n";
    }
}
Ожидаемый результат
В области item: item
В области item: price
Кандидат отсутствует или вне области item

Массив содержит три разных аргумента: сам item, его price, затем null. Contains возвращает true, true, false. Поэтому первые две ветки читают имя объекта, а последняя не пытается читать свойство null. Contains включает сам контейнер; для строгого потомка нужно также исключить isSameNode.

Не изменять служебную настройку вместо цены товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<root><item><price>100</price></item><settings><price>999</price></settings></root>');
$item = $document->documentElement->firstElementChild;
foreach ($document->getElementsByTagName('price') as $price) {
    if ($item->contains($price)) {
        $price->textContent = '120';
    }
}
foreach ($document->getElementsByTagName('price') as $price) {
    echo $price->textContent, "
";
}
Ожидаемый результат
120
999

Имя price встречается в нескольких ветках. contains задаёт структурную границу; в реальном редакторе права доступа проверяются ещё до этой операции.

Связанные операции: Dom\Node::isSameNode, Dom\Node::getRootNode.

Dom\Node::getRootNodeОтличить узел, включённый в документ, от отдельно подготовленной ветки, которой ещё предстоит вставка.

PHP 8.4+ · dom

Dom\Node::getRootNode(array $options = []): Dom\Node

Отличить узел, включённый в документ, от отдельно подготовленной ветки, которой ещё предстоит вставка.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Создать узел документом ещё не значит вставить его в документ. Метод без аргументов возвращает самый верхний узел именно текущей цепочки родителей. Первое действие ниже читает имя этого корня, следующее использует различие при подготовке правки.

Аргумент / поведениеПолный контракт
$optionsНеобязательный array со значением []. В PHP DOM этот параметр сейчас не влияет на результат; браузерные варианты с Shadow DOM сюда переносить не нужно.
ВозвратКорень текущего дерева Node: подъём по parentNode до отсутствующего родителя. Для подключённого элемента это документ; для отсоединённого поддерева — верхний элемент, а не ownerDocument.
Изменение / версияТолько чтение. В старом DOM метод появился в PHP 8.3.
Две связи у отсоединённой ветки
  1. item создан документом: ownerDocument → document
  2. item не вставлен: parentNode = null
  3. item.getRootNode() → item; после вставки → document
Увидеть корень заготовки до и после включения в каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$item = $document->createElement('item');
$item->textContent = 'Book';
$rootBefore = $item->getRootNode();
echo 'Корень заготовки: ', $rootBefore->nodeName, "\n";
echo 'Каталог до: ', $document->saveXml($document->documentElement), "\n";
$document->documentElement->appendChild($item);
$rootAfter = $item->getRootNode();
echo 'Корень после: ', $rootAfter->nodeName, "\n";
echo 'Каталог после: ', $document->saveXml($rootAfter->documentElement), "\n";
Ожидаемый результат
Корень заготовки: item
Каталог до: <catalog/>
Корень после: #document
Каталог после: <catalog><item>Book</item></catalog>

GetRootNode вернул объект в rootBefore: отдельный item, у которого нет родителя. После appendChild путь item→catalog→document закончился документом в rootAfter. Его documentElement снова даёт каталог для вывода. OwnerDocument у item всё это время был document; создавший документ и текущий корень — разные связи.

Проверить отделённую ветку перед возвратом в документСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item><name>Book</name></item></catalog>');
$catalog = $document->documentElement;
$item = $catalog->removeChild($catalog->firstChild);
$name = $item->firstChild;
if (!$name->getRootNode()->isSameNode($document)) {
    echo 'Ещё не в каталоге: ', $name->textContent, "\n";
    $name->textContent = 'Notebook';
    $catalog->appendChild($item);
}
echo $document->saveXml($catalog), "\n";
Ожидаемый результат
Ещё не в каталоге: Book
<catalog><item><name>Notebook</name></item></catalog>

После removeChild корень name — item. Цепочка getRootNode()->isSameNode сначала получает этот корень, затем сравнивает его с document; ! обращает false в true. Правим отсоединённую ветку и вставляем целиком. Это изменение дерева в памяти, не публикация и не запись на диск.

Связанные операции: Dom\Node::contains, Dom\Node::removeChild.

Dom\Node::isSameNodeПроверить идентичность узла: два результата поиска указывают на одну запись дерева или на две похожие записи?

PHP 8.4+ · dom

Dom\Node::isSameNode(?Dom\Node $otherNode): bool

Проверить идентичность узла: два результата поиска указывают на одну запись дерева или на две похожие записи?

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. При двух поисках можно получить две переменные для одной записи. IsSameNode принимает второй узел и возвращает bool; первая правка показывает практическое последствие true. Копия даёт false и меняется отдельно. Во втором примере идентичность исключает сам контейнер из строгих потомков.

Аргумент / поведениеПолный контракт
$otherNodeДругой Node или null; null даёт false.
Возвратbool, true только для того же базового DOM-узла. Имя, текст и атрибуты сами по себе идентичность не определяют.
ВыборДля сравнения содержимого существует isEqualNode. Для PHP-переменных современный runtime также сохраняет идентичность обёртки, но метод явно выражает DOM-вопрос. Не сравнивайте идентичность по сериализованному XML.
Один узел найден двумя путями
  1. root.firstChild → item-A
  2. getElementsByTagName("item")[0] → item-A
  3. isSameNode = true; cloneNode → другой узел
Узнать общую запись у двух результатов поискаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>Book</item></catalog>');
$first = $document->documentElement->firstChild;
$found = $document->getElementsByTagName('item')->item(0);
if ($first->isSameNode($found)) {
    $found->textContent = 'Notebook';
}
echo 'Через first: ', $first->textContent, "\n";
$copy = $first->cloneNode(true);
if (!$first->isSameNode($copy)) {
    $copy->textContent = 'Copy';
}
echo 'Копия: ', $copy->textContent, "\n";
echo 'Оригинал: ', $first->textContent, "\n";
Ожидаемый результат
Через first: Notebook
Копия: Copy
Оригинал: Notebook

Item(0) берёт первый объект списка поиска, индексы начинаются с нуля. First и found указывают на один item: правка через found видна через first. CloneNode(true) возвращает другой узел с теми же начальными данными. Его отдельная правка не изменила оригинал. Метод сравнивает идентичность узла, не его имя или текст.

Не включать сам контейнер в список его строгих потомковСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<group><item>A</item></group>');
$group = $document->documentElement;
$candidates = [$group, $group->firstChild];
foreach ($candidates as $candidate) {
    if ($group->contains($candidate) && !$group->isSameNode($candidate)) {
        echo $candidate->nodeName, "
";
    }
}
Ожидаемый результат
item

contains включает сам group. Отрицание ! перед isSameNode исключает только контейнер, а не похожие элементы.

Связанные операции: Dom\Node::isEqualNode, Dom\Node::contains.

Dom\Node::isEqualNodeСравнить содержимое двух XML-блоков перед заменой: если структура не изменилась, можно пропустить запись новой версии.

PHP 8.4+ · dom

Dom\Node::isEqualNode(?Dom\Node $otherNode): bool

Сравнить содержимое двух XML-блоков перед заменой: если структура не изменилась, можно пропустить запись новой версии.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Перед заменой XML-настройки полезно понять, изменились ли данные. IsEqualNode возвращает bool для содержимого; клон может быть равным, не будучи тем же объектом. Показываем обе версии XML, затем применяем сравнение к двум последовательным настройкам валюты.

Аргумент / поведениеПолный контракт
$otherNodeДругой узел или null. null даёт false.
Возвратbool. Сравниваются характеристики соответствующих типов узлов, атрибуты и последовательность дочерних узлов рекурсивно. Разное положение в документе не мешает равенству.
Что важноПорядок атрибутов не является порядком детей; текстовые пробелы и разные типы узлов влияют на равенство. Это не бизнес-сравнение цен 1 и 1.00 и не проверка эквивалентности произвольных XML-схем.
Стоимость / версияНужно сравнивать поддеревья, а не только адрес объекта; объём работы зависит от размера до первого отличия. В старом DOMNode метод доступен с PHP 8.3.
Похожий не значит тот же
  1. item-A → name=Book
  2. clone item-B → name=Book: equal true, same false
  3. name у B меняется: equal false
Показать содержимое равных и изменённых копийСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<item sku="A">Book</item>');
$item = $document->documentElement;
$copy = $item->cloneNode(true);
if ($item->isEqualNode($copy)) {
    echo 'Одинаковое содержимое: ', $document->saveXml($copy), "\n";
}
$copy->textContent = 'Notebook';
if (!$item->isEqualNode($copy)) {
    echo 'Было: ', $document->saveXml($item), "\n";
    echo 'Стало: ', $document->saveXml($copy), "\n";
}
Ожидаемый результат
Одинаковое содержимое: <item sku="A">Book</item>
Было: <item sku="A">Book</item>
Стало: <item sku="A">Notebook</item>

Первый true означает равенство структуры и значений, хотя cloneNode создал отдельный объект. После textContent=Notebook результат false позволяет показать различие. IsEqualNode не приводит Book и Notebook к общему виду и не игнорирует пробелы по бизнес-правилам.

Пропустить одинаковую настройку и принять изменённую валютуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<settings><currency code="RUB"/></settings>');
$settings = $document->documentElement;
foreach (['RUB', 'EUR'] as $code) {
    $current = $settings->firstChild;
    $candidate = $document->createElement('currency');
    $candidate->setAttribute('code', $code);
    if ($current->isEqualNode($candidate)) {
        echo 'Без изменения: ', $code, "\n";
        continue;
    }
    $settings->replaceChild($candidate, $current);
    echo 'Сохранена настройка: ', $document->saveXml($settings), "\n";
}
Ожидаемый результат
Без изменения: RUB
Сохранена настройка: <settings><currency code="EUR"/></settings>

В первой итерации кандидат RUB совпадает с текущим узлом; continue пропускает замену. Во второй отличается атрибут code, поэтому replaceChild меняет дерево. Обе ветки видны в одном запуске. Для большого документа сравнение поддеревьев может быть дороже проверки нескольких значимых полей; сначала определите требуемое правило равенства.

Связанные операции: Dom\Node::isSameNode, Dom\Node::normalize, Dom\Node::C14N.

Dom\Node::compareDocumentPositionУзнать порядок двух найденных узлов и отдельно проверить вложенность; это полезно для упорядочивания результатов из разных поисков.

PHP 8.4+ · dom

Dom\Node::compareDocumentPosition(Dom\Node $other): int

Узнать порядок двух найденных узлов и отдельно проверить вложенность; это полезно для упорядочивания результатов из разных поисков.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Импортёр нашёл задачи разными запросами; нужно восстановить их положение в одном дереве. Метод возвращает целое с несколькими флагами. Сначала учимся читать одну маску20, затем проверяем связность перед использованием порядка. Таблица сохраняет значения и направление всех флагов.

Аргумент / поведениеПолный контракт
$otherУзел, чьё положение сравнивается относительно текущего. Аргумент обязателен.
Возвратint — битовая маска, а не номер позиции. Одинаковый узел даёт 0; PRECEDING=2, FOLLOWING=4; CONTAINS=8 означает, что other — предок текущего; CONTAINED_BY=16, что other — потомок.
Несвязанные узлыDISCONNECTED=1 и IMPLEMENTATION_SPECIFIC=32 предупреждают об отсутствии переносимого общего порядка. В маске может быть несколько флагов; проверяйте ($position & FLAG) !== 0, а не равенство одному флагу.
ВерсияPHP 8.4+ для обоих семейств. Для вопроса «внутри ли узел?» проще contains.
Направление сравнения
  1. root.compareDocumentPosition(child)
  2. other=child следует после root и находится в нём
  3. FOLLOWING 4 | CONTAINED_BY 16 = 20
Прочитать маску как несколько отношений между узламиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<queue><task>A</task><task>B</task></queue>');
$queue = $document->documentElement;
$first = $queue->firstChild;
$last = $queue->lastChild;
$position = $queue->compareDocumentPosition($first);
echo 'Маска queue к A: ', $position, "\n";
if (($position & Dom\Node::DOCUMENT_POSITION_CONTAINED_BY) !== 0) {
    echo 'Внутри queue: ', $first->textContent, "\n";
}
if (($last->compareDocumentPosition($first) & Dom\Node::DOCUMENT_POSITION_PRECEDING) !== 0) {
    echo 'Раньше B выполнить: ', $first->textContent, "\n";
}
Ожидаемый результат
Маска queue к A: 20
Внутри queue: A
Раньше B выполнить: A

CompareDocumentPosition у queue получает first=A. Число20 складывает флаги4 и16: другой узел следует за контейнером и вложен в него. Побитовое & оставляет интересующий бит; !==0 проверяет его наличие. Во втором вызове точка отсчёта B, другой узел A; PRECEDING означает «A раньше B». Это порядок дерева, не часы выполнения задания.

Выстроить две найденные задачи и отложить несвязанную заготовкуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<queue><task>A</task><task>B</task></queue>');
$first = $document->documentElement->firstChild;
$last = $document->documentElement->lastChild;
$draft = $document->createElement('task');
$draft->textContent = 'C';
foreach ([$last, $draft] as $other) {
    $position = $first->compareDocumentPosition($other);
    if (($position & Dom\Node::DOCUMENT_POSITION_DISCONNECTED) !== 0) {
        echo 'Вне очереди, нужен приоритет: ', $other->textContent, "\n";
    } elseif (($position & Dom\Node::DOCUMENT_POSITION_FOLLOWING) !== 0) {
        echo 'Порядок очереди: ', $first->textContent, ' -> ', $other->textContent, "\n";
    }
}
Ожидаемый результат
Порядок очереди: A -> B
Вне очереди, нужен приоритет: C

Сначала проверяется DISCONNECTED: заготовка C ещё не вставлена и не имеет переносимого места относительно A. Для B деревья общие, FOLLOWING позволяет вывести A→B. Сравнивать всю маску с4 неправильно: вместе с порядком может присутствовать флаг вложенности. Бизнес-приоритет несвязанных задач задают отдельными данными.

Связанные операции: Dom\Node::contains, Dom\Node::isSameNode.

Dom\Node::getLineNoПоказать автору XML строку проблемной записи вместо сообщения «ошибка где-то в файле».

PHP 8.4+ · dom

Dom\Node::getLineNo(): int

Показать автору XML строку проблемной записи вместо сообщения «ошибка где-то в файле».

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. XML из примера содержит настоящие переводы строк. GetLineNo без аргументов возвращает исходный номер, который echo выводит или сообщение об ошибке присоединяет к названию поля. Сначала строка2 сопоставляется с новым узлом без координаты0; затем строка3 помогает найти отсутствующую цену.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int — номер строки исходного разбора для узла, когда он доступен. Узел, созданный программно, обычно даёт 0.
ТочностьЭто метаданные загрузки, а не номер строки будущего saveXML. После переносов/редактирования номер не пересчитывается под новый вывод. Для очень больших XML поведение старых libxml зависело от хранения номеров; LIBXML_BIGLINES используется при разборе там, где поддерживается.
Версия / данные журналаСтарый метод доступен с PHP 5.3. Номер строки безопаснее полного XML в логе, который может содержать адреса и токены.
Диагностика импорта
  1. Исходный XML: item на строке 2
  2. Проверка price обнаружила пустое значение
  3. getLineNo() → 2; пользователю возвращается координата, не весь документ
Получить исходную строку элементаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog>
<item>Book</item>
</catalog>');
$item = $document->getElementsByTagName('item')->item(0);
echo $item->getLineNo(), "
";
echo $document->createElement('item')->getLineNo(), "
";
Ожидаемый результат
2
0

Ноль у новой заготовки означает отсутствие исходной строки; это не нулевая строка XML-файла.

Сообщить координату записи с отсутствующей ценойСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog>
<item price="100"/>
<item/>
</catalog>');
foreach ($document->getElementsByTagName('item') as $item) {
    if (!$item->hasAttribute('price')) {
        echo 'Missing price at line ', $item->getLineNo(), "
";
    }
}
Ожидаемый результат
Missing price at line 3

hasAttribute здесь проверяет только наличие. Следующий этап импорта обязан проверить формат и диапазон цены.

Связанные операции: Dom\Node::getNodePath.

Dom\Node::getNodePathПолучить путь к конкретной записи для диагностики импорта или повторного выбора в неизменённом документе.

PHP 8.4+ · dom

Dom\Node::getNodePath(): string

Получить путь к конкретной записи для диагностики импорта или повторного выбора в неизменённом документе.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Если импортёр сообщает только «товар неверен», автору трудно найти запись. GetNodePath возвращает её текущую координату. Простой вывод показывает второй item; затем эта же строка используется как запрос и сопоставляется с изменением дерева. Индекс XPath2 начинается с1; NodeList item(0) начинается с0.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; string с XPath-подобным местоположением узла.
Не постоянный IDИндекс зависит от соседей, например item[2]. Перестановка или вставка узлов меняет путь. Храните бизнес-ID в атрибуте, если ссылка должна переживать редактирование.
Пространства имёнПрефикс в пути необходимо привязать к URI в XPath-контексте; путь не заменяет registerNamespace. У документа без префиксов учебные пути можно выполнить напрямую.
ВерсияСтарый API PHP 5.2+. Путь может раскрывать структуру документа: выдавайте только нужную диагностику.
Координата второго товара
  1. catalog → item[1], item[2]
  2. item[2].getNodePath()
  3. /catalog/item[2] относится к текущему порядку, а не навсегда к товару
Увидеть путь второго элементаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item><item>B</item></catalog>');
echo $document->documentElement->lastChild->getNodePath(), "
";
Ожидаемый результат
/catalog/item[2]

Квадратные скобки здесь относятся к XPath и нумеруются с 1, в отличие от индексов NodeList с 0.

Прочитать товар по пути и увидеть изменение координатыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item><item>B</item></catalog>');
$item = $document->documentElement->lastChild;
$path = $item->getNodePath();
$query = new Dom\XPath($document);
$found = $query->query($path)->item(0);
echo 'По пути ', $path, ': ', $found->textContent, "\n";
$document->documentElement->removeChild($document->documentElement->firstChild);
echo 'Тот же товар ', $item->textContent, ' теперь: ', $item->getNodePath(), "\n";
Ожидаемый результат
По пути /catalog/item[2]: B
Тот же товар B теперь: /catalog/item

GetNodePath возвращает строку, присвоенную path. XPath —объект для запросов по структуре; query(path) возвращает список, item(0) берёт его первый результат. Здесь путь получен из того же неизменённого документа, поэтому B найден. Удаление A меняет координату B; старый путь не является устойчивым ID. Для произвольного запроса отдельно обрабатывайте ошибку query и пустой результат.

Связанные операции: Dom\XPath::query, Dom\XPath::registerNamespace, Dom\Node::getLineNo.

Dom\Node::lookupNamespaceURIПо короткому префиксу выяснить, к какому XML-пространству имён он относится именно в этой точке документа.

PHP 8.4+ · dom

Dom\Node::lookupNamespaceURI(?string $prefix): ?string

По короткому префиксу выяснить, к какому XML-пространству имён он относится именно в этой точке документа.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Префикс —локальное сокращение URI: один и тот же p может означать разные словари в соседних ветках. LookupNamespaceURI вызывается в нужном контексте и возвращает строку либо null. Сначала читаем привязку, затем применяем её к разбору акционного значения.

Аргумент / поведениеПолный контракт
$prefixСтрока префикса; null запрашивает пространство имён по умолчанию. Ищется привязка в контексте узла и его предков, с учётом переопределений.
Возврат?string: URI найденной привязки либо null при её отсутствии. URI — идентификатор; метод не скачивает его по сети.
ГраницаПрефикс не является постоянным именем схемы: два поставщика могут назвать один URI по-разному. Для сравнения смысла используйте namespaceURI + localName, а XPath регистрируйте с собственным префиксом.
Префикс имеет локальное значение
  1. root: xmlns:p=urn:prices
  2. вложенный item: xmlns:p=urn:promo
  3. lookupNamespaceURI("p") у item → urn:promo
Прочитать привязку префикса и обработать неизвестное имяСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns="urn:catalog" xmlns:p="urn:prices"/>');
$root = $document->documentElement;
echo 'Префикс p означает: ', $root->lookupNamespaceURI('p'), "\n";
echo 'Без префикса: ', $root->lookupNamespaceURI(null), "\n";
$uri = $root->lookupNamespaceURI('unknown');
if ($uri === null) {
    echo "unknown не объявлен: запросить описание формата\n";
}
Ожидаемый результат
Префикс p означает: urn:prices
Без префикса: urn:catalog
unknown не объявлен: запросить описание формата

Строка p без двоеточия —аргумент поиска. Return urn:prices попадает прямо в echo. Аргумент null спрашивает default namespace; результат null у unknown означает отсутствие привязки. URI здесь служит именем формата, сетевого запроса не происходит.

Распознать акционное поле при локальной смене префиксаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<root xmlns:p="urn:prices"><item xmlns:p="urn:promo"><p:value>10</p:value></item></root>');
$root = $document->documentElement;
$item = $root->firstElementChild;
$value = $item->firstElementChild;
echo 'p у root: ', $root->lookupNamespaceURI('p'), "\n";
echo 'p у item: ', $item->lookupNamespaceURI('p'), "\n";
if ($value->namespaceURI === 'urn:promo' && $value->localName === 'value') {
    echo 'Акционная величина: ', $value->textContent, "\n";
}
Ожидаемый результат
p у root: urn:prices
p у item: urn:promo
Акционная величина: 10

Item переобъявил p. Lookup показывает разные URI в двух контекстах; фактический элемент проверяется по паре namespaceURI и localName, поэтому10 не будет принято за обычную цену только по буквам p:value. Для бизнес-смысла суммы ещё нужны правила валюты и допустимого диапазона.

Связанные операции: Dom\Node::lookupPrefix, Dom\Node::isDefaultNamespace, Dom\XPath::registerNamespace.

Dom\Node::lookupPrefixНайти доступный префикс для известного URI, например для понятного диагностического сообщения о внешнем XML.

PHP 8.4+ · dom

Dom\Node::lookupPrefix(?string $namespace): ?string

Найти доступный префикс для известного URI, например для понятного диагностического сообщения о внешнем XML.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Когда URI уже известен, можно найти его сокращение для отчёта. Return —строка или null; ниже обе ветки дают понятную подпись. Второй пример объясняет, почему отсутствие именованного префикса совместимо с существующим пространством по умолчанию.

Аргумент / поведениеПолный контракт
$namespaceURI пространства имён или null в современном API.
Возврат?string: подходящий префикс либо null, если префикса нет. Пространство по умолчанию может существовать без возвращаемого префикса. При нескольких привязках не делайте бизнес-логику зависимой от выбора конкретного варианта.
ГраницаНе создаёт декларацию xmlns и не регистрирует префикс в XPath. Для нового имени используйте createElementNS/setAttributeNS; для запроса registerNamespace.
Обратный поиск
  1. xmlns:p="urn:prices"
  2. lookupPrefix("urn:prices") → p
  3. xmlns="urn:catalog" не даёт обязательного именованного префикса
Сформировать понятную подпись поля по известному URIСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<root xmlns:p="urn:prices"/>');
$root = $document->documentElement;
foreach (['urn:prices', 'urn:missing'] as $uri) {
    $prefix = $root->lookupPrefix($uri);
    $label = $prefix === null ? '{' . $uri . '}value' : $prefix . ':value';
    echo 'Поле отчёта: ', $label, "\n";
}
Ожидаемый результат
Поле отчёта: p:value
Поле отчёта: {urn:missing}value

LookupPrefix получил полный URI и вернул p либо null. Условие выбирает подпись: привычное p:value или явно показанный URI в фигурных скобках. Эти скобки —наша текстовая нотация отчёта, не создаваемый XML-тег и не XPath. Метод не создаёт нового префикса для отсутствующего пространства.

Показать имя элемента с пространством по умолчаниюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns="urn:catalog"><item>Book</item></catalog>');
$item = $document->documentElement->firstElementChild;
$prefix = $item->lookupPrefix('urn:catalog');
if ($prefix === null) {
    echo "Именованного префикса нет\n";
}
echo 'Полное имя: {', $item->namespaceURI, '}', $item->localName, "\n";
echo 'Данные: ', $item->textContent, "\n";
Ожидаемый результат
Именованного префикса нет
Полное имя: {urn:catalog}item
Данные: Book

Null не доказывает, что item не принадлежит пространству: здесь действует default namespace. NamespaceURI и localName дают полное смысловое имя, а textContent —данные Book. Для XPath-запроса к этим элементам всё равно регистрируют собственный префикс, даже если исходный XML написал теги без него.

Связанные операции: Dom\Node::lookupNamespaceURI, Dom\XPath::registerNamespace.

Dom\Node::isDefaultNamespaceПроверить, какой URI используется для непредварённых префиксом элементов в текущей области XML.

PHP 8.4+ · dom

Dom\Node::isDefaultNamespace(?string $namespace): bool

Проверить, какой URI используется для непредварённых префиксом элементов в текущей области XML.

CreateFromString превращает указанную XML-строку в document; documentElement выбирает корневой элемент. Каталог использует пространство по умолчанию: его элементы написаны без префикса. IsDefaultNamespace возвращает решение для конкретного ожидаемого URI. После простого выбора формата разбираем частую ошибку: sku не наследует пространство своего элемента.

Аргумент / поведениеПолный контракт
$namespaceURI для сравнения; современный API также принимает null для отсутствующего default namespace.
Возвратbool: совпадает ли аргумент с пространством имён по умолчанию в контексте узла.
Важное различиеНепрефиксные атрибуты XML не наследуют default namespace элемента. Проверка не отвечает, в каком пространстве находится произвольный атрибут, и не проверяет PHP namespace.
Пространство по умолчанию
  1. catalog xmlns="urn:catalog"
  2. item без префикса находится в urn:catalog
  3. атрибут sku без префикса находится без пространства имён
Выбрать правило обработки для непредварённого префиксом элементаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns="urn:catalog"><item>Book</item></catalog>');
$item = $document->documentElement->firstElementChild;
foreach (['urn:catalog', 'urn:other'] as $expected) {
    if ($item->isDefaultNamespace($expected)) {
        echo 'Подходит формат ', $expected, ': ', $item->textContent, "\n";
    } else {
        echo 'Не применять правила ', $expected, "\n";
    }
}
Ожидаемый результат
Подходит формат urn:catalog: Book
Не применять правила urn:other

IsDefaultNamespace сравнивает URI аргумента с текущей привязкой по умолчанию. True разрешает показать Book в ожидаемом формате; false отклоняет чужое правило. Проверка относится к default namespace, а не ко всем именам с префиксами. URI сравнивается как строка, без обращения к сети.

Прочитать sku без ошибочного пространства атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns="urn:catalog"><item sku="A">Book</item></catalog>');
$item = $document->documentElement->firstElementChild;
if ($item->isDefaultNamespace('urn:catalog')) {
    $sku = $item->getAttributeNS(null, 'sku');
    echo 'Товар ', $sku, ': ', $item->textContent, "\n";
}
if (!$item->hasAttributeNS('urn:catalog', 'sku')) {
    echo "Атрибута sku в urn:catalog нет\n";
}
Ожидаемый результат
Товар A: Book
Атрибута sku в urn:catalog нет

Default namespace относится к непредварённому префиксом элементу item, но не к атрибуту sku. Поэтому getAttributeNS получает null и возвращает A. HasAttributeNS с URI каталога показывает, почему такой ошибочный поиск ничего не найдёт. Legacy getAttributeNS при отсутствии даёт пустую строку, современный —null; hasAttributeNS отличает отсутствие от существующего пустого значения.

Связанные операции: Dom\Node::lookupNamespaceURI, Dom\Element::getAttributeNS.

DOMNode::C14NПолучить стабильное XML-представление для сравнения/контрольной суммы, когда обычный порядок атрибутов не должен считаться изменением.

PHP 5.2+ · dom

DOMNode::C14N(bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): string|false

Получить стабильное XML-представление для сравнения/контрольной суммы, когда обычный порядок атрибутов не должен считаться изменением.

Нужно сравнить XML-представления, не считая перестановку атрибутов изменением. Сначала loadXML/createFromString строит дерево из указанной строки; C14N без аргументов возвращает каноническую строку в canonical. Echo показывает именно её: a стоит раньше z, пустой item развёрнут в пару тегов. Проверка ===false отделяет отказ от строки. Далее отдельно меняем комментарии, состав выбранных узлов и политику пространств имён.

Аргумент / поведениеПолный контракт
$exclusive = falsefalse использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера.
$withComments = falseВключить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму.
$xpath = nullnull канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["query" => выражение, "namespaces" => [префикс => URI]], не список таких массивов. Запрос должен вернуть набор узлов; нужные text(), атрибуты и namespace-узлы входят в выбор явно.
$nsPrefixes = nullСписок префиксов, которые нужно включительно сохранить при exclusive=true; #default обозначает default namespace. Это не список элементов и не URI. При exclusive=false список не применяется и PHP 8.5.11 сообщает E_NOTICE.
Ошибки входаВ PHP 8.5.11 у массива xpath без query — ValueError, query не строка — TypeError; скалярный результат XPath вызывает Error. Ошибки libxml могут давать диагностику и false. Относительные namespace URI несовместимы с требованиями каноникализации.
Присоединение к документуУзел должен иметь ownerDocument; совсем несвязанный узел вызывает Error. Отсоединённый узел, созданный документом, может дать пустую строку. Не принимайте её за успешную каноникализацию нужного поддерева.
Возвратstring канонического XML либо false при сбое. Пустая строка — отдельное значение, не false; для подписываемого сообщения дополнительно проверяйте, что выбран именно ожидаемый непустой набор узлов.
От записи к каноническим байтам
  1. <item z="2" a="1"/>
  2. C14N: согласованные правила атрибутов и пустых элементов
  3. <item a="1" z="2"></item>; хешируется именно эта строка

C14N делает предсказуемую XML-запись: например, упорядочивает атрибуты и раскрывает пустой тег в пару открывающего/закрывающего. Она не форматирует красивый HTML, не удаляет значимые текстовые пробелы и не проверяет подлинность документа. Алгоритм, включение комментариев, XPath и namespace-политика должны совпадать у отправителя и проверяющего. Для XML Signature нужен проверенный протокол и библиотека с защитой от подмены подписанного узла; одна строка hash(C14N()) такого протокола не создаёт.

Увидеть результат каноникализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<item z="2" a="1"/>');
$canonical = $document->C14N();
if ($canonical === false) {
    throw new RuntimeException('Canonicalization failed');
}
echo $canonical, "
";
Ожидаемый результат
<item a="1" z="2"></item>

Строгое === false отделяет ошибку от допустимой строки. XML-декларация здесь не включается в канонический результат.

Согласовать политику комментариев перед сравнениемСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order><!-- internal note --><item>A</item></order>');
$without = $document->C14N(false, false);
$with = $document->C14N(false, true);
echo $without, "
";
echo $with, "
";
Ожидаемый результат
<order><item>A</item></order>
<order><!-- internal note --><item>A</item></order>

Одинаковая бизнес-запись даёт разные байты при другой политике комментариев. На стороне получателя нельзя угадывать параметры подписи.

Выбрать только поле публичного названияСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<item><name>Book</name><internal>supplier note</internal></item>');
$publicXml = $document->C14N(false, false, [
    'query' => '//name | //name/text()',
]);
echo $publicXml, "
";
Ожидаемый результат
<name>Book</name>

XPath включает и сам name, и его текст. Если выбрать только элемент без его текстового узла, результат не обязан содержать Book. Для экспорта сложного публичного формата яснее построить новый документ с разрешёнными полями.

Исправить настройку фильтра и получить сам заказСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order id="A"><item>Book</item></order>');
$filters = [
    'Нет query' => [],
    'Число вместо запроса' => ['query' => 42],
    'Число вместо узлов' => ['query' => 'count(//order)'],
];
foreach ($filters as $label => $filter) {
    try {
        $document->C14N(false, false, $filter);
    } catch (Throwable $error) {
        echo $label, ': ', get_class($error), "; экспорт отменён\n";
    }
}
$canonical = $document->C14N(false, false, [
    'query' => '//order | //order/@* | //order//node()',
]);
if ($canonical === false) {
    throw new RuntimeException('Canonicalization failed');
}
echo 'После исправления: ', $canonical, "\n";
Ожидаемый результат
Нет query: ValueError; экспорт отменён
Число вместо запроса: TypeError; экспорт отменён
Число вместо узлов: Error; экспорт отменён
После исправления: <order id="A"><item>Book</item></order>

Три неправильные конфигурации обрабатываются отдельно: query обязателен, он должен быть строкой, а его результатом должен быть набор узлов. Исправленный XPath объединяет через | элемент order, его атрибуты @* и всех вложенных узлов //node(), включая Text Book. Return сохраняется в canonical, проверяется на false и только потом выводится. Этот выбор не содержит пространств имён; для них ниже явно включены namespace-узлы.

Связанные операции: DOMNode::C14NFile, DOMDocument::saveXML.

Отличить включающую и исключающую каноникализациюСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root xmlns:p="urn:prices" xmlns:q="urn:metadata"><p:price currency="RUB">100</p:price></root>');
$price = $document->documentElement->firstElementChild;
echo 'Inclusive: ', $price->C14N(false), "\n";
echo 'Exclusive: ', $price->C14N(true), "\n";
echo 'Exclusive с q: ', $price->C14N(true, false, null, ['q']), "\n";
Ожидаемый результат
Inclusive: <p:price xmlns:p="urn:prices" xmlns:q="urn:metadata" currency="RUB">100</p:price>
Exclusive: <p:price xmlns:p="urn:prices" currency="RUB">100</p:price>
Exclusive с q: <p:price xmlns:p="urn:prices" xmlns:q="urn:metadata" currency="RUB">100</p:price>

Price присоединён к документу и наследует объявления p и q. False сохраняет оба объявления по правилам inclusive. True оставляет видимый p, нужный имени элемента; q в имени не используется. Четвёртый аргумент [q] просит сохранить q и при exclusive. Это массив префиксов, а не URI. Третий null означает отсутствие XPath-фильтра. В рабочем протоколе режим задаётся заранее: выбирать его по понравившемуся выводу нельзя.

Экспортировать нужное поле с текстом, атрибутом и пространством имёнСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root xmlns:p="urn:prices"><p:price currency="RUB">100</p:price><secret>internal</secret></root>');
$public = $document->C14N(true, false, [
    'query' => '//v:price | //v:price/@* | //v:price/text() | //v:price/namespace::*',
    'namespaces' => ['v' => 'urn:prices'],
]);
if ($public === false || $public === '') {
    throw new RuntimeException('Expected public price is absent');
}
echo $public, "\n";
Ожидаемый результат
<p:price xmlns:p="urn:prices" currency="RUB">100</p:price>

Массив имеет два ключа: query и namespaces. Собственный XPath-префикс v связан с тем же URI, что исходный p; менять XML-префикс не требуется. Части запроса выбирают элемент, атрибут currency, Text100 и объявления пространства. Return становится строкой public, в которой нет secret. Такая выборка не заменяет проектирование разрешённой схемы экспорта или протокол XML Signature.

DOMNode::C14NFileЗаписать согласованное каноническое XML-представление в контролируемый локальный файл и проверить число записанных байтов.

PHP 5.2+ · dom

DOMNode::C14NFile(string $uri, bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): int|false

Записать согласованное каноническое XML-представление в контролируемый локальный файл и проверить число записанных байтов.

Задача —создать локальный XML-файл с согласованными каноническими байтами. Метод вызывается у документа, первый аргумент задаёт путь, return сообщает записанный объём. Содержимое проверяется отдельным чтением файла. Ниже имя фиксировано автором примера; путь от формы сюда не передаётся. Файл появляется в рабочей папке, поэтому его можно открыть после запуска.

Аргумент / поведениеПолный контракт
$uriПуть назначения: используйте фиксированный каталог приложения и проверенное имя. Метод выполняет запись; не принимайте произвольный URI от посетителя. Нулевой байт в пути недопустим.
$exclusive = falsefalse использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера.
$withComments = falseВключить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму.
$xpath = nullnull канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["query" => выражение, "namespaces" => [префикс => URI]], не список таких массивов. Запрос должен вернуть набор узлов; нужные text(), атрибуты и namespace-узлы входят в выбор явно.
$nsPrefixes = nullСписок префиксов, которые нужно включительно сохранить при exclusive=true; #default обозначает default namespace. Это не список элементов и не URI. При exclusive=false список не применяется и PHP 8.5.11 сообщает E_NOTICE.
Ошибки входаВ PHP 8.5.11 у массива xpath без query — ValueError, query не строка — TypeError; скалярный результат XPath вызывает Error. Ошибки libxml могут давать диагностику и false. Относительные namespace URI несовместимы с требованиями каноникализации.
Присоединение к документуУзел должен иметь ownerDocument; совсем несвязанный узел вызывает Error. Отсоединённый узел, созданный документом, может дать пустую строку. Не принимайте её за успешную каноникализацию нужного поддерева.
Возвратint — число записанных байтов, не символов/узлов; false при сбое. Ошибки открытия/записи могут сопровождаться диагностикой. Нулевой int не равен false.
НадёжностьПрямой вызов не является атомарной публикацией. Для важного экспорта пишите временный файл рядом с целевым, проверяйте результат и только потом выполняйте согласованную замену с обработкой ошибок файловой системы. Память самого DOM-дерева уже занята: запись в файл не превращает разбор в потоковый.
Экспорт контрольного XML
  1. DOM заказа в памяти
  2. C14NFile(контролируемый путь, параметры)
  3. Файл содержит канонические байты; int подтверждает записанный объём
Записать файл и прочитать его содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order id="A"/>');
$bytes = $document->C14NFile('canonical.xml');
if ($bytes === false) {
    throw new RuntimeException('Write failed');
}
$saved = file_get_contents('canonical.xml');
if ($saved === false) {
    throw new RuntimeException('Read failed');
}
echo 'canonical.xml: ', $saved, "\n";
echo 'Записано байтов: ', $bytes, "\n";
echo 'Прочитано байтов: ', strlen($saved), "\n";
Ожидаемый результат
canonical.xml: <order id="A"></order>
Записано байтов: 22
Прочитано байтов: 22

Первый аргумент —имя создаваемого файла относительно рабочей папки запуска. C14NFile возвращает число записанных байтов22 в bytes, а не XML-строку. FileGetContents читает строку отдельно; strlen здесь также измеряет байты. При false выполнение прерывается. Запускайте пример в отдельной учебной папке: существующий canonical.xml по этому пути будет перезаписан.

Выгрузить отчёт без внутреннего комментария и проверить данные файлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<report><!-- build note --><total>2</total></report>');
$expected = $document->C14N(false, false);
$bytes = $document->C14NFile('report.xml', false, false);
if ($expected === false || $bytes === false) {
    throw new RuntimeException('Export failed');
}
$saved = file_get_contents('report.xml');
if ($saved === false || $saved !== $expected) {
    throw new RuntimeException('Export does not match agreed XML');
}
echo 'report.xml: ', $saved, "\n";
echo 'Готовый объём: ', $bytes, " байт\n";
Ожидаемый результат
report.xml: <report><total>2</total></report>
Готовый объём: 33 байт

После uri идут два bool: exclusive=false, withComments=false. Оба вызова используют одинаковую политику; прочитанный файл сравнивается с ожидаемой строкой, затем показывается целиком. Видно, что total2 сохранён, build note отсутствует. Это небольшой учебный экспорт: для больших документов не держите второй полный результат только ради такого сравнения; для важной замены используйте временный файл и обработку ошибок файловой системы.

Связанные операции: DOMNode::C14N, DOMDocument::save.

DOMNode::__sleepПонять отказ serialize($node): DOM-узел нельзя сохранить как обычный PHP-объект с одной лишь служебной ссылкой на дерево.

PHP 8.1.25+ · dom

DOMNode::__sleep(): array

Понять отказ serialize($node): DOM-узел нельзя сохранить как обычный PHP-объект с одной лишь служебной ссылкой на дерево.

В очереди или кэше нужно сохранить заказ между запусками PHP. DOM-объект связан с внутренним деревом libxml; обычный serialize не сохраняет его автоматически и вызывает защитное исключение. Первый пример показывает отказ и осмысленный XML-результат; второй записывает этот формат в реальный файл с проверкой записи.

Аргумент / поведениеПолный контракт
Аргументы / сигнатураБез аргументов; декларация array относится к соглашению магического __sleep, но реализация DOM всегда бросает исключение и не возвращает массив.
Вызов и ошибкаserialize вызывает защитный механизм автоматически. В PHP 8.5.11 непосредственный __sleep у DOM-узла и serialize такого узла завершаются Exception; официальный текст называет Error, поэтому код на границе ловит Throwable, не полагаясь на этот неточный перечень.
Версии / наследникЗащитные методы добавлены в старую ветку начиная с PHP 8.1.25 и соответствующие исправленные ветки; Dom\Node существует с 8.4. Наследник может реализовать собственную сериализацию, но обязан явно сохранять и восстанавливать полезные данные, а не ожидать восстановления внутренних libxml-указателей.
Практическая альтернативаДля кэша/очереди храните XML через saveXML или нормализованный массив полей; при чтении заново разбирайте XML с безопасной политикой libxml. Не используйте PHP unserialize для внешних XML-сообщений.
Почему serialize не сохраняет дерево автоматически
  1. PHP-переменная → DOM-объект → внутреннее дерево libxml
  2. serialize(node) вызывает защиту → исключение
  3. Сохранить XML-строку; при чтении создать новое DOM-дерево
Понять отказ serialize и сохранить полезные данные другим способомСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order id="A"/>');
$order = $document->documentElement;
try {
    $payload = serialize($order);
} catch (Throwable $error) {
    echo 'DOM-объект не сохранён: ', get_class($error), "\n";
    $payload = $document->saveXML($order);
}
if ($payload === false) {
    throw new RuntimeException('XML export failed');
}
echo 'Переносимые данные: ', $payload, "\n";
Ожидаемый результат
DOM-объект не сохранён: Exception
Переносимые данные: <order id="A"/>

Serialize пытается сохранить PHP-объект и запускает запрет сериализации DOM. В catch получаем другой формат —XML-строку. После обработки payload содержит данные заказа, а не сериализованный PHP-объект. Такой переход формата должен быть явным для читателя кэша: ниже он читает именно XML. В обычном приложении сразу выбирают saveXML; намеренный отказ здесь нужен для понимания старого вызова serialize.

Создать проверяемый XML-кэш заказа на дискеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order id="A"><item>Book</item></order>');
$payload = $document->saveXML($document->documentElement);
if ($payload === false) {
    throw new RuntimeException('XML export failed');
}
$written = file_put_contents('order-cache.xml', $payload);
if ($written === false || $written !== strlen($payload)) {
    throw new RuntimeException('Cache write failed');
}
$saved = file_get_contents('order-cache.xml');
if ($saved === false) {
    throw new RuntimeException('Cache read failed');
}
echo 'order-cache.xml: ', $saved, "\n";
Ожидаемый результат
order-cache.xml: <order id="A"><item>Book</item></order>

SaveXML возвращает строку, filePutContents записывает её и возвращает число байтов либо false. Полноту записи проверяем до чтения. Файл видим в рабочей папке, его содержимое пригодно для последующего разбора XML. Для настоящего кэша нужны отдельные правила имени, срока жизни, размера и замены файла; магический __sleep этих правил не создаёт.

Связанные операции: DOMNode::__wakeup, DOMDocument::saveXML.

DOMNode::__wakeupРазобраться, почему внутренний DOM-объект нельзя восстановить из обычного PHP-представления: для дерева нужен настоящий разбор XML.

PHP 8.1.25+ · dom

DOMNode::__wakeup(): void

Разобраться, почему внутренний DOM-объект нельзя восстановить из обычного PHP-представления: для дерева нужен настоящий разбор XML.

Чтобы вернуть заказ из кэша, нужны его XML-данные и новый разбор. Магический __wakeup не выполняет эту работу за DOM-узел: базовая реализация отказывает. Сначала показываем сохранение прежнего дерева после отказа; затем получаем отдельное новое дерево и видим независимую правку его содержимого.

Аргумент / поведениеПолный контракт
Аргументы / возвратНет аргументов, заявленный void; базовая реализация всегда бросает исключение.
Ошибка и границаВ PHP 8.5.11 непосредственный вызов даёт Exception (в официальной странице перечислен Error). Это защитный магический метод, не команда перечитать XML-файл. В нормальном приложении его вручную не вызывают.
Наследование / безопасностьПользовательская сериализация наследника может обойти базовый метод, но сама должна восстановить корректный документ. Не принимать сериализованные PHP-объекты от внешнего клиента: даже allowed_classes не превращает произвольный unserialize в универсально безопасный формат обмена.
АльтернативаЧитать XML-строку с лимитом и разбирать через loadXML/createFromString. Ошибки XML нужно обработать до доступа к documentElement; параметры, способные загрузить внешние сущности, требуют отдельного обоснования.
Восстановление осмысленных данных
  1. В кэше: строка <order id="A"/>
  2. Парсер XML создаёт новое дерево
  3. Новое дерево имеет данные заказа; это не прежняя PHP-идентичность
Увидеть, что __wakeup не загружает XML и не меняет заказСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order id="A"><item>Book</item></order>');
try {
    $document->documentElement->__wakeup();
} catch (Throwable $error) {
    echo 'Восстановление этим способом запрещено: ', get_class($error), "\n";
}
echo 'Текущее дерево: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
Восстановление этим способом запрещено: Exception
Текущее дерево: <order id="A"><item>Book</item></order>

Непосредственный вызов приведён для исследования запрета: базовый метод всегда бросает исключение, хотя объявлен void. Он не получил путь или XML и не загрузил никаких данных. После catch доступен прежний заказ. Рабочий способ восстановления —передать сохранённую XML-строку парсеру, как в следующем примере.

Восстановить отдельный заказ и изменить его, сохранив исходныйСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<order id="A"><item>Book</item></order>');
$payload = $document->saveXML();
if ($payload === false) {
    throw new RuntimeException('XML export failed');
}
$restored = new DOMDocument();
if (!$restored->loadXML($payload)) {
    throw new RuntimeException('Restore failed');
}
$restored->documentElement->firstElementChild->textContent = 'Notebook';
echo 'Восстановленная версия: ', $restored->saveXML($restored->documentElement), "\n";
echo 'Исходная версия: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
Восстановленная версия: <order id="A"><item>Notebook</item></order>
Исходная версия: <order id="A"><item>Book</item></order>

SaveXML создаёт строку payload; парсер строит новое дерево restored. Правка Text через restored не затрагивает document, что видно по двум версиям заказа. Это перенос данных, а не восстановление прежней объектной идентичности. XML здесь создан самим примером; внешнему кэшу нужен лимит размера и разбор с обработкой ошибок до чтения documentElement.

Связанные операции: DOMNode::__sleep, DOMDocument::loadXML.

Dom\Node::C14NПолучить стабильное XML-представление для сравнения/контрольной суммы, когда обычный порядок атрибутов не должен считаться изменением.

PHP 8.4+ · dom

Dom\Node::C14N(bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): string|false

Получить стабильное XML-представление для сравнения/контрольной суммы, когда обычный порядок атрибутов не должен считаться изменением.

Нужно сравнить XML-представления, не считая перестановку атрибутов изменением. Сначала loadXML/createFromString строит дерево из указанной строки; C14N без аргументов возвращает каноническую строку в canonical. Echo показывает именно её: a стоит раньше z, пустой item развёрнут в пару тегов. Проверка ===false отделяет отказ от строки. Далее отдельно меняем комментарии, состав выбранных узлов и политику пространств имён.

Аргумент / поведениеПолный контракт
$exclusive = falsefalse использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера.
$withComments = falseВключить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму.
$xpath = nullnull канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["query" => выражение, "namespaces" => [префикс => URI]], не список таких массивов. Запрос должен вернуть набор узлов; нужные text(), атрибуты и namespace-узлы входят в выбор явно.
$nsPrefixes = nullСписок префиксов, которые нужно включительно сохранить при exclusive=true; #default обозначает default namespace. Это не список элементов и не URI. При exclusive=false список не применяется и PHP 8.5.11 сообщает E_NOTICE.
Ошибки входаВ PHP 8.5.11 у массива xpath без query — ValueError, query не строка — TypeError; скалярный результат XPath вызывает Error. Ошибки libxml могут давать диагностику и false. Относительные namespace URI несовместимы с требованиями каноникализации.
Присоединение к документуСовременный узел должен находиться в дереве документа: отсоединённый узел вызывает DOMException / HIERARCHY_REQUEST_ERR.
Возвратstring канонического XML либо false при сбое. Пустая строка — отдельное значение, не false; для подписываемого сообщения дополнительно проверяйте, что выбран именно ожидаемый непустой набор узлов.
От записи к каноническим байтам
  1. <item z="2" a="1"/>
  2. C14N: согласованные правила атрибутов и пустых элементов
  3. <item a="1" z="2"></item>; хешируется именно эта строка

C14N делает предсказуемую XML-запись: например, упорядочивает атрибуты и раскрывает пустой тег в пару открывающего/закрывающего. Она не форматирует красивый HTML, не удаляет значимые текстовые пробелы и не проверяет подлинность документа. Алгоритм, включение комментариев, XPath и namespace-политика должны совпадать у отправителя и проверяющего. Для XML Signature нужен проверенный протокол и библиотека с защитой от подмены подписанного узла; одна строка hash(C14N()) такого протокола не создаёт.

Увидеть результат каноникализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<item z="2" a="1"/>');
$canonical = $document->C14N();
if ($canonical === false) {
    throw new RuntimeException('Canonicalization failed');
}
echo $canonical, "
";
Ожидаемый результат
<item a="1" z="2"></item>

Строгое === false отделяет ошибку от допустимой строки. XML-декларация здесь не включается в канонический результат.

Согласовать политику комментариев перед сравнениемСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order><!-- internal note --><item>A</item></order>');
$without = $document->C14N(false, false);
$with = $document->C14N(false, true);
echo $without, "
";
echo $with, "
";
Ожидаемый результат
<order><item>A</item></order>
<order><!-- internal note --><item>A</item></order>

Одинаковая бизнес-запись даёт разные байты при другой политике комментариев. На стороне получателя нельзя угадывать параметры подписи.

Выбрать только поле публичного названияСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<item><name>Book</name><internal>supplier note</internal></item>');
$publicXml = $document->C14N(false, false, [
    'query' => '//name | //name/text()',
]);
echo $publicXml, "
";
Ожидаемый результат
<name>Book</name>

XPath включает и сам name, и его текст. Если выбрать только элемент без его текстового узла, результат не обязан содержать Book. Для экспорта сложного публичного формата яснее построить новый документ с разрешёнными полями.

Исправить настройку фильтра и получить сам заказСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order id="A"><item>Book</item></order>');
$filters = [
    'Нет query' => [],
    'Число вместо запроса' => ['query' => 42],
    'Число вместо узлов' => ['query' => 'count(//order)'],
];
foreach ($filters as $label => $filter) {
    try {
        $document->C14N(false, false, $filter);
    } catch (Throwable $error) {
        echo $label, ': ', get_class($error), "; экспорт отменён\n";
    }
}
$canonical = $document->C14N(false, false, [
    'query' => '//order | //order/@* | //order//node()',
]);
if ($canonical === false) {
    throw new RuntimeException('Canonicalization failed');
}
echo 'После исправления: ', $canonical, "\n";
Ожидаемый результат
Нет query: ValueError; экспорт отменён
Число вместо запроса: TypeError; экспорт отменён
Число вместо узлов: Error; экспорт отменён
После исправления: <order id="A"><item>Book</item></order>

Три неправильные конфигурации обрабатываются отдельно: query обязателен, он должен быть строкой, а его результатом должен быть набор узлов. Исправленный XPath объединяет через | элемент order, его атрибуты @* и всех вложенных узлов //node(), включая Text Book. Return сохраняется в canonical, проверяется на false и только потом выводится. Этот выбор не содержит пространств имён; для них ниже явно включены namespace-узлы.

Связанные операции: Dom\Node::C14NFile, Dom\XMLDocument::saveXML.

Отличить включающую и исключающую каноникализациюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<root xmlns:p="urn:prices" xmlns:q="urn:metadata"><p:price currency="RUB">100</p:price></root>');
$price = $document->documentElement->firstElementChild;
echo 'Inclusive: ', $price->C14N(false), "\n";
echo 'Exclusive: ', $price->C14N(true), "\n";
echo 'Exclusive с q: ', $price->C14N(true, false, null, ['q']), "\n";
Ожидаемый результат
Inclusive: <p:price xmlns:p="urn:prices" xmlns:q="urn:metadata" currency="RUB">100</p:price>
Exclusive: <p:price xmlns:p="urn:prices" currency="RUB">100</p:price>
Exclusive с q: <p:price xmlns:p="urn:prices" xmlns:q="urn:metadata" currency="RUB">100</p:price>

Price присоединён к документу и наследует объявления p и q. False сохраняет оба объявления по правилам inclusive. True оставляет видимый p, нужный имени элемента; q в имени не используется. Четвёртый аргумент [q] просит сохранить q и при exclusive. Это массив префиксов, а не URI. Третий null означает отсутствие XPath-фильтра. В рабочем протоколе режим задаётся заранее: выбирать его по понравившемуся выводу нельзя.

Экспортировать нужное поле с текстом, атрибутом и пространством имёнСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<root xmlns:p="urn:prices"><p:price currency="RUB">100</p:price><secret>internal</secret></root>');
$public = $document->C14N(true, false, [
    'query' => '//v:price | //v:price/@* | //v:price/text() | //v:price/namespace::*',
    'namespaces' => ['v' => 'urn:prices'],
]);
if ($public === false || $public === '') {
    throw new RuntimeException('Expected public price is absent');
}
echo $public, "\n";
Ожидаемый результат
<p:price xmlns:p="urn:prices" currency="RUB">100</p:price>

Массив имеет два ключа: query и namespaces. Собственный XPath-префикс v связан с тем же URI, что исходный p; менять XML-префикс не требуется. Части запроса выбирают элемент, атрибут currency, Text100 и объявления пространства. Return становится строкой public, в которой нет secret. Такая выборка не заменяет проектирование разрешённой схемы экспорта или протокол XML Signature.

Dom\Node::C14NFileЗаписать согласованное каноническое XML-представление в контролируемый локальный файл и проверить число записанных байтов.

PHP 8.4+ · dom

Dom\Node::C14NFile(string $uri, bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null): int|false

Записать согласованное каноническое XML-представление в контролируемый локальный файл и проверить число записанных байтов.

Задача —создать локальный XML-файл с согласованными каноническими байтами. Метод вызывается у документа, первый аргумент задаёт путь, return сообщает записанный объём. Содержимое проверяется отдельным чтением файла. Ниже имя фиксировано автором примера; путь от формы сюда не передаётся. Файл появляется в рабочей папке, поэтому его можно открыть после запуска.

Аргумент / поведениеПолный контракт
$uriПуть назначения: используйте фиксированный каталог приложения и проверенное имя. Метод выполняет запись; не принимайте произвольный URI от посетителя. Нулевой байт в пути недопустим.
$exclusive = falsefalse использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера.
$withComments = falseВключить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму.
$xpath = nullnull канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["query" => выражение, "namespaces" => [префикс => URI]], не список таких массивов. Запрос должен вернуть набор узлов; нужные text(), атрибуты и namespace-узлы входят в выбор явно.
$nsPrefixes = nullСписок префиксов, которые нужно включительно сохранить при exclusive=true; #default обозначает default namespace. Это не список элементов и не URI. При exclusive=false список не применяется и PHP 8.5.11 сообщает E_NOTICE.
Ошибки входаВ PHP 8.5.11 у массива xpath без query — ValueError, query не строка — TypeError; скалярный результат XPath вызывает Error. Ошибки libxml могут давать диагностику и false. Относительные namespace URI несовместимы с требованиями каноникализации.
Присоединение к документуСовременный узел должен находиться в дереве документа: отсоединённый узел вызывает DOMException / HIERARCHY_REQUEST_ERR.
Возвратint — число записанных байтов, не символов/узлов; false при сбое. Ошибки открытия/записи могут сопровождаться диагностикой. Нулевой int не равен false.
НадёжностьПрямой вызов не является атомарной публикацией. Для важного экспорта пишите временный файл рядом с целевым, проверяйте результат и только потом выполняйте согласованную замену с обработкой ошибок файловой системы. Память самого DOM-дерева уже занята: запись в файл не превращает разбор в потоковый.
Экспорт контрольного XML
  1. DOM заказа в памяти
  2. C14NFile(контролируемый путь, параметры)
  3. Файл содержит канонические байты; int подтверждает записанный объём
Записать файл и прочитать его содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order id="A"/>');
$bytes = $document->C14NFile('canonical.xml');
if ($bytes === false) {
    throw new RuntimeException('Write failed');
}
$saved = file_get_contents('canonical.xml');
if ($saved === false) {
    throw new RuntimeException('Read failed');
}
echo 'canonical.xml: ', $saved, "\n";
echo 'Записано байтов: ', $bytes, "\n";
echo 'Прочитано байтов: ', strlen($saved), "\n";
Ожидаемый результат
canonical.xml: <order id="A"></order>
Записано байтов: 22
Прочитано байтов: 22

Первый аргумент —имя создаваемого файла относительно рабочей папки запуска. C14NFile возвращает число записанных байтов22 в bytes, а не XML-строку. FileGetContents читает строку отдельно; strlen здесь также измеряет байты. При false выполнение прерывается. Запускайте пример в отдельной учебной папке: существующий canonical.xml по этому пути будет перезаписан.

Выгрузить отчёт без внутреннего комментария и проверить данные файлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<report><!-- build note --><total>2</total></report>');
$expected = $document->C14N(false, false);
$bytes = $document->C14NFile('report.xml', false, false);
if ($expected === false || $bytes === false) {
    throw new RuntimeException('Export failed');
}
$saved = file_get_contents('report.xml');
if ($saved === false || $saved !== $expected) {
    throw new RuntimeException('Export does not match agreed XML');
}
echo 'report.xml: ', $saved, "\n";
echo 'Готовый объём: ', $bytes, " байт\n";
Ожидаемый результат
report.xml: <report><total>2</total></report>
Готовый объём: 33 байт

После uri идут два bool: exclusive=false, withComments=false. Оба вызова используют одинаковую политику; прочитанный файл сравнивается с ожидаемой строкой, затем показывается целиком. Видно, что total2 сохранён, build note отсутствует. Это небольшой учебный экспорт: для больших документов не держите второй полный результат только ради такого сравнения; для важной замены используйте временный файл и обработку ошибок файловой системы.

Связанные операции: Dom\Node::C14N, Dom\XMLDocument::saveXmlFile.

Dom\Node::__sleepПонять отказ serialize($node): DOM-узел нельзя сохранить как обычный PHP-объект с одной лишь служебной ссылкой на дерево.

PHP 8.4+ · dom

Dom\Node::__sleep(): array

Понять отказ serialize($node): DOM-узел нельзя сохранить как обычный PHP-объект с одной лишь служебной ссылкой на дерево.

В очереди или кэше нужно сохранить заказ между запусками PHP. DOM-объект связан с внутренним деревом libxml; обычный serialize не сохраняет его автоматически и вызывает защитное исключение. Первый пример показывает отказ и осмысленный XML-результат; второй записывает этот формат в реальный файл с проверкой записи.

Аргумент / поведениеПолный контракт
Аргументы / сигнатураБез аргументов; декларация array относится к соглашению магического __sleep, но реализация DOM всегда бросает исключение и не возвращает массив.
Вызов и ошибкаserialize вызывает защитный механизм автоматически. В PHP 8.5.11 непосредственный __sleep у DOM-узла и serialize такого узла завершаются Exception; официальный текст называет Error, поэтому код на границе ловит Throwable, не полагаясь на этот неточный перечень.
Версии / наследникЗащитные методы добавлены в старую ветку начиная с PHP 8.1.25 и соответствующие исправленные ветки; Dom\Node существует с 8.4. Наследник может реализовать собственную сериализацию, но обязан явно сохранять и восстанавливать полезные данные, а не ожидать восстановления внутренних libxml-указателей.
Практическая альтернативаДля кэша/очереди храните XML через saveXML или нормализованный массив полей; при чтении заново разбирайте XML с безопасной политикой libxml. Не используйте PHP unserialize для внешних XML-сообщений.
Почему serialize не сохраняет дерево автоматически
  1. PHP-переменная → DOM-объект → внутреннее дерево libxml
  2. serialize(node) вызывает защиту → исключение
  3. Сохранить XML-строку; при чтении создать новое DOM-дерево
Понять отказ serialize и сохранить полезные данные другим способомСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order id="A"/>');
$order = $document->documentElement;
try {
    $payload = serialize($order);
} catch (Throwable $error) {
    echo 'DOM-объект не сохранён: ', get_class($error), "\n";
    $payload = $document->saveXML($order);
}
if ($payload === false) {
    throw new RuntimeException('XML export failed');
}
echo 'Переносимые данные: ', $payload, "\n";
Ожидаемый результат
DOM-объект не сохранён: Exception
Переносимые данные: <order id="A"/>

Serialize пытается сохранить PHP-объект и запускает запрет сериализации DOM. В catch получаем другой формат —XML-строку. После обработки payload содержит данные заказа, а не сериализованный PHP-объект. Такой переход формата должен быть явным для читателя кэша: ниже он читает именно XML. В обычном приложении сразу выбирают saveXML; намеренный отказ здесь нужен для понимания старого вызова serialize.

Создать проверяемый XML-кэш заказа на дискеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order id="A"><item>Book</item></order>');
$payload = $document->saveXML($document->documentElement);
if ($payload === false) {
    throw new RuntimeException('XML export failed');
}
$written = file_put_contents('order-cache.xml', $payload);
if ($written === false || $written !== strlen($payload)) {
    throw new RuntimeException('Cache write failed');
}
$saved = file_get_contents('order-cache.xml');
if ($saved === false) {
    throw new RuntimeException('Cache read failed');
}
echo 'order-cache.xml: ', $saved, "\n";
Ожидаемый результат
order-cache.xml: <order id="A"><item>Book</item></order>

SaveXML возвращает строку, filePutContents записывает её и возвращает число байтов либо false. Полноту записи проверяем до чтения. Файл видим в рабочей папке, его содержимое пригодно для последующего разбора XML. Для настоящего кэша нужны отдельные правила имени, срока жизни, размера и замены файла; магический __sleep этих правил не создаёт.

Связанные операции: Dom\Node::__wakeup, Dom\XMLDocument::saveXML.

Dom\Node::__wakeupРазобраться, почему внутренний DOM-объект нельзя восстановить из обычного PHP-представления: для дерева нужен настоящий разбор XML.

PHP 8.4+ · dom

Dom\Node::__wakeup(): void

Разобраться, почему внутренний DOM-объект нельзя восстановить из обычного PHP-представления: для дерева нужен настоящий разбор XML.

Чтобы вернуть заказ из кэша, нужны его XML-данные и новый разбор. Магический __wakeup не выполняет эту работу за DOM-узел: базовая реализация отказывает. Сначала показываем сохранение прежнего дерева после отказа; затем получаем отдельное новое дерево и видим независимую правку его содержимого.

Аргумент / поведениеПолный контракт
Аргументы / возвратНет аргументов, заявленный void; базовая реализация всегда бросает исключение.
Ошибка и границаВ PHP 8.5.11 непосредственный вызов даёт Exception (в официальной странице перечислен Error). Это защитный магический метод, не команда перечитать XML-файл. В нормальном приложении его вручную не вызывают.
Наследование / безопасностьПользовательская сериализация наследника может обойти базовый метод, но сама должна восстановить корректный документ. Не принимать сериализованные PHP-объекты от внешнего клиента: даже allowed_classes не превращает произвольный unserialize в универсально безопасный формат обмена.
АльтернативаЧитать XML-строку с лимитом и разбирать через loadXML/createFromString. Ошибки XML нужно обработать до доступа к documentElement; параметры, способные загрузить внешние сущности, требуют отдельного обоснования.
Восстановление осмысленных данных
  1. В кэше: строка <order id="A"/>
  2. Парсер XML создаёт новое дерево
  3. Новое дерево имеет данные заказа; это не прежняя PHP-идентичность
Увидеть, что __wakeup не загружает XML и не меняет заказСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order id="A"><item>Book</item></order>');
try {
    $document->documentElement->__wakeup();
} catch (Throwable $error) {
    echo 'Восстановление этим способом запрещено: ', get_class($error), "\n";
}
echo 'Текущее дерево: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
Восстановление этим способом запрещено: Exception
Текущее дерево: <order id="A"><item>Book</item></order>

Непосредственный вызов приведён для исследования запрета: базовый метод всегда бросает исключение, хотя объявлен void. Он не получил путь или XML и не загрузил никаких данных. После catch доступен прежний заказ. Рабочий способ восстановления —передать сохранённую XML-строку парсеру, как в следующем примере.

Восстановить отдельный заказ и изменить его, сохранив исходныйСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<order id="A"><item>Book</item></order>');
$payload = $document->saveXML();
if ($payload === false) {
    throw new RuntimeException('XML export failed');
}
$restored = Dom\XMLDocument::createFromString($payload);
$restored->documentElement->firstElementChild->textContent = 'Notebook';
echo 'Восстановленная версия: ', $restored->saveXML($restored->documentElement), "\n";
echo 'Исходная версия: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
Восстановленная версия: <order id="A"><item>Notebook</item></order>
Исходная версия: <order id="A"><item>Book</item></order>

SaveXML создаёт строку payload; парсер строит новое дерево restored. Правка Text через restored не затрагивает document, что видно по двум версиям заказа. Это перенос данных, а не восстановление прежней объектной идентичности. XML здесь создан самим примером; внешнему кэшу нужен лимит размера и разбор с обработкой ошибок до чтения documentElement.

Связанные операции: Dom\Node::__sleep, Dom\XMLDocument::createFromString.

DOMCharacterData::appendDataДополнить текст существующего узла, не создавать рядом новый Text и не менять вложенные элементы родителя.

PHP 5+ · dom

DOMCharacterData::appendData(string $data): true

Дополнить текст существующего узла, не создавать рядом новый Text и не менять вложенные элементы родителя.

New DOMDocument создаёт document; loadXML заполняет его XML-строкой. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. К приветствию Привет нужно дописать имя Анна. AppendData получает только добавляемую строку и меняет тот же Text. При нормальном завершении метод возвращает true; полезный текст нужно читать из изменённого data, а не из этого флага. Первый пример показывает итоговую строку и одного ребёнка вместо двух; следующий сохраняет имя с амперсандом как корректный XML.

Аргумент / поведениеПолный контракт
$dataСтрока UTF-8, которая дописывается в конец текущего CharacterData. XML-разметка внутри строки остаётся обычным текстом.
ВозвратВсегда true при нормальном завершении. В PHP 8.3 tentative return type сужен до true.
Границы / выборНе заменяет appendChild: тот добавляет другой узел. Для полной замены содержимого одного Text присвойте data. Для полной замены всех детей элемента свойство textContent меняет большее поддерево.
Дополнение одного текстового узла
  1. label → Text("Привет")
  2. text.appendData(", Анна")
  3. label → тот же Text("Привет, Анна"); число детей не увеличилось
Дополнить приветствие в одном узлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Привет</label>');
$text = $document->documentElement->firstChild;
$text->appendData(', Анна');
echo $text->data, "
";
echo $document->documentElement->childNodes->length, "
";
Ожидаемый результат
Привет, Анна
1

firstChild — текст внутри label. Метод изменил этот объект; новый сосед не появился.

Добавить название со знаком амперсанда как данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Author: </label>');
$text = $document->documentElement->firstChild;
$text->appendData('A & B');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Author: A &amp; B</label>

В data хранится A & B, а XML-сериализатор корректно экранирует амперсанд. Не подставляйте текст руками в строку с тегами.

Связанные операции: DOMCharacterData::insertData, DOMDocument::createTextNode.

DOMCharacterData::substringDataВзять часть текстового поля XML для короткой подписи, сохранив полный исходный текст в документе.

PHP 5+ · dom

DOMCharacterData::substringData(int $offset, int $count): string|false

Взять часть текстового поля XML для короткой подписи, сохранив полный исходный текст в документе.

New DOMDocument создаёт document; loadXML заполняет его XML-строкой. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. Нужна короткая подпись, а полное значение должно остаться в XML. SubstringData(0,3) начинает с первой кодовой точки и берёт три: П, р, и. Return —отдельная строка При, переданная в echo; исходный data остаётся Привет. Затем preview получает эту строку внутри цикла и добавляет многоточие только при реальном сокращении.

Аргумент / поведениеПолный контракт
$offsetНачальная позиция с нуля. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$countНеотрицательное количество кодовых точек. Если остаток короче, возвращается весь остаток; count=0 даёт пустую строку.
Возвратstring|false; строка без изменения узла, false при неуспехе.
Ошибки / границыoffset от 0 до length включительно; выход за диапазон — DOMException с DOM_INDEX_SIZE_ERR. count ограничивается оставшейся длиной. Отрицательный count вызывает DOMException с DOM_INDEX_SIZE_ERR; при отключённом strictErrorChecking старые методы могут вернуть false с диагностикой.
Выбор инструментаДля настоящего пользовательского ограничения по видимым символам нужна Unicode-графемная обработка; substringData может разделить составной символ.
Извлечение не меняет источник
  1. Text("Привет"): П0 р1 и2 в3 е4 т5
  2. substringData(0, 3) → "При"
  3. Text в документе по-прежнему "Привет"
Получить первые три кодовые точкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Привет</label>');
$text = $document->documentElement->firstChild;
echo $text->substringData(0, 3), "
";
echo $text->data, "
";
Ожидаемый результат
При
Привет

Вызов возвращает строку; он не удаляет остаток. Номера букв не равны байтовым индексам русского UTF-8.

Построить короткие подписи без переписывания XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<labels><label>Notebook</label><label>Pen</label></labels>');
foreach ($document->documentElement->childNodes as $label) {
    $text = $label->firstChild;
    $preview = $text->substringData(0, 4);
    echo $preview, $text->length > 4 ? '...' : '', "
";
}
Ожидаемый результат
Note...
Pen

В этом формате каждый label содержит один Text. При смешанной разметке сначала определите, надо ли объединить textContent, а не слепо работать только с первым ребёнком.

Связанные операции: DOMCharacterData::deleteData, DOMCharacterData::replaceData.

DOMCharacterData::insertDataВставить префикс или уточнение внутрь одного текстового поля, не собирая текст вручную из байтовых substr.

PHP 5+ · dom

DOMCharacterData::insertData(int $offset, string $data): bool

Вставить префикс или уточнение внутрь одного текстового поля, не собирая текст вручную из байтовых substr.

New DOMDocument создаёт document; loadXML заполняет его XML-строкой. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. В строке Заказ42 с пробелом перед цифрами нужно поставить №, сохранив цифры. InsertData(6,№) вставляет строку перед кодовой точкой6; нумерация начинается с0, пробел также занимает позицию. При нормальном завершении метод возвращает true; полезный текст нужно читать из изменённого data, а не из этого флага. Во втором примере позиция0 ставит метку только в name, оставляя price отдельным элементом.

Аргумент / поведениеПолный контракт
$offsetПозиция перед которой вставляется строка; 0 — начало, length — конец. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$dataВставляемая строка UTF-8; исходный хвост сдвигается вправо.
Возвратtrue при успехе, false при неуспехе; ошибки границ при обычном strictErrorChecking бросают DOMException.
Ошибкиoffset вне 0..length — DOMException / DOM_INDEX_SIZE_ERR. Пустая строка ничего не добавляет. Значения из интерфейса пользователя проверяйте до обращения к DOM.
Вставка без удаления
  1. Text("Заказ 42")
  2. insertData(6, "№")
  3. Text("Заказ №42"); исходные цифры сохранены
Добавить знак номера в подписьСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Заказ 42</label>');
$text = $document->documentElement->firstChild;
$text->insertData(6, '№');
echo $text->data, "
";
Ожидаемый результат
Заказ №42

Шесть — количество кодовых точек в «Заказ »: пять букв и пробел. Это не результат strlen русского текста.

Поставить отметку перед текстом, не затронув соседнюю ценуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<item><name>Book</name><price>100</price></item>');
$item = $document->documentElement;
$text = $item->firstElementChild->firstChild;
$text->insertData(0, '[SALE] ');
echo $item->firstElementChild->textContent, "
";
echo $item->lastElementChild->textContent, "
";
Ожидаемый результат
[SALE] Book
100

Изменяется Text внутри name. Присваивание item->textContent уничтожило бы структуру name/price, поэтому здесь оно не подходит.

Связанные операции: DOMCharacterData::appendData, DOMCharacterData::replaceData.

DOMCharacterData::deleteDataУбрать известный служебный префикс из текстового поля, сохранив сам узел и окружающую разметку.

PHP 5+ · dom

DOMCharacterData::deleteData(int $offset, int $count): bool

Убрать известный служебный префикс из текстового поля, сохранив сам узел и окружающую разметку.

New DOMDocument создаёт document; loadXML заполняет его XML-строкой. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. Удаление части строки не требует удалять её Text-узел. В AB-CD индекс2 указывает на дефис; deleteData(2,1) убирает одну кодовую точку и оставляет ABCD в data. При нормальном завершении метод возвращает true; полезный текст нужно читать из изменённого data, а не из этого флага. При обработке названий ниже сначала проверяется DRAFT:, чтобы обычный Pen не потерял первые буквы.

Аргумент / поведениеПолный контракт
$offsetС какой кодовой точки начать удаление, индекс с нуля. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$countСколько кодовых точек удалить. Число больше остатка удаляет до конца; 0 не меняет текст.
Возвратtrue при успехе, false при неуспехе; ошибки границ при обычном strictErrorChecking бросают DOMException.
Ошибки / края диапазонаoffset от 0 до length включительно; выход за диапазон — DOMException с DOM_INDEX_SIZE_ERR. count ограничивается оставшейся длиной. Отрицательный count вызывает DOMException с DOM_INDEX_SIZE_ERR; при отключённом strictErrorChecking старые методы могут вернуть false с диагностикой.
ОсторожностьСначала проверьте, что нужный префикс действительно присутствует. Удалить первые семь символов у любого имени — испортить данные, а не очистить их.
Удаление служебного начала
  1. Text("DRAFT: Book")
  2. Проверка starts_with("DRAFT: ")
  3. deleteData(0, 7) → Text("Book")
Удалить середину строкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>AB-CD</label>');
$text = $document->documentElement->firstChild;
$text->deleteData(2, 1);
echo $text->data, "
";
Ожидаемый результат
ABCD

Удаляется одна кодовая точка с индексом 2 — дефис. Остальные части соединяются в том же Text.

Снять только существующую метку черновикаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<names><name>DRAFT: Book</name><name>Pen</name></names>');
foreach ($document->documentElement->childNodes as $name) {
    $text = $name->firstChild;
    if (str_starts_with($text->data, 'DRAFT: ')) {
        $text->deleteData(0, 7);
    }
    echo $text->data, "
";
}
Ожидаемый результат
Book
Pen

Здесь ASCII-префикс имеет одинаковую длину в байтах и кодовых точках. Для другого префикса не подставляйте strlen, если он содержит многобайтовые символы.

Связанные операции: DOMCharacterData::substringData, DOMCharacterData::replaceData.

DOMCharacterData::replaceDataЗаменить выбранный диапазон текста одной операцией: например, обновить известную часть шаблонной подписи.

PHP 5+ · dom

DOMCharacterData::replaceData(int $offset, int $count, string $data): bool

Заменить выбранный диапазон текста одной операцией: например, обновить известную часть шаблонной подписи.

New DOMDocument создаёт document; loadXML заполняет его XML-строкой. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. В известной строке Price:100 с пробелом после двоеточия число начинается с позиции7. ReplaceData(7,3,1200) удаляет три кодовые точки100 и вставляет четыре1200. При нормальном завершении метод возвращает true; полезный текст нужно читать из изменённого data, а не из этого флага. Следующий случай проверяет префикс Status: и вычисляет остаток длины явно, прежде чем записать ready.

Аргумент / поведениеПолный контракт
$offsetНачало заменяемого диапазона с нуля. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$countКоличество удаляемых кодовых точек; превышение остатка означает весь остаток. 0 превращает операцию во вставку.
$dataНовая строка произвольной длины; не обязана совпадать по размеру с удалённым диапазоном.
Возвратtrue при успехе, false при неуспехе; ошибки границ при обычном strictErrorChecking бросают DOMException.
Ошибки / границыoffset от 0 до length включительно; выход за диапазон — DOMException с DOM_INDEX_SIZE_ERR. count ограничивается оставшейся длиной. Отрицательный count вызывает DOMException с DOM_INDEX_SIZE_ERR; при отключённом strictErrorChecking старые методы могут вернуть false с диагностикой.
Замена диапазона
  1. Text("Price: 100")
  2. replaceData(7, 3, "1200")
  3. Text("Price: 1200"); новая длина может отличаться
Заменить число в заранее известной подписиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Price: 100</label>');
$text = $document->documentElement->firstChild;
$text->replaceData(7, 3, '1200');
echo $text->data, "
";
Ожидаемый результат
Price: 1200

Это учебная фиксированная строка. Денежное значение в реальном формате лучше хранить отдельным price, а подпись строить из проверенных данных.

Обновить остаток после проверенного префиксаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Status: waiting</label>');
$text = $document->documentElement->firstChild;
if (!str_starts_with($text->data, 'Status: ')) {
    throw new RuntimeException('Unexpected label format');
}
$offset = 8;
$text->replaceData($offset, $text->length - $offset, 'ready');
echo $text->data, "
";
Ожидаемый результат
Status: ready

Вместо отрицательного count явно вычислен остаток. Если формат изменится, проверка остановит правку до повреждения текста.

Связанные операции: DOMCharacterData::insertData, DOMCharacterData::deleteData.

DOMText::splitTextОтделить часть текстового узла, чтобы обернуть её отдельным элементом, например выделить важное слово в описании.

PHP 5+ · dom

DOMText::splitText(int $offset): DOMText|false

Отделить часть текстового узла, чтобы обернуть её отдельным элементом, например выделить важное слово в описании.

New DOMDocument создаёт document; loadXML заполняет его XML-строкой. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. Нужно выделить слово Book внутри описания Buy Book. Сначала Text разделяется на начало и хвост; splitText возвращает объект хвоста, а начало остаётся в исходном объекте. После простого опыта с ABCD новый strong получает именно возвращённый tail. Сам splitText тега выделения не создаёт.

Аргумент / поведениеПолный контракт
$offsetГраница разделения с нуля в кодовых точках UTF-8. 0 оставляет первый узел пустым; length создаёт пустой второй Text.
Изменение и возвратИсходный объект сохраняет текст до offset. Возвращается новый Text с хвостом; если исходный имел родителя, хвост вставлен сразу после него. Старый контракт DOMText|false.
Ошибки PHP 8.5.11Отрицательный offset вызывает ValueError. Значение больше длины возвращает false. Эти различия важны при переносе между API.
ГраницаОперация не возвращает массив из двух строк и не создаёт выделение сама. Вторую часть можно переместить в новый элемент. normalize позднее может снова склеить соседние Text.
Разделение и выделение
  1. description → Text("Buy Book")
  2. splitText(4): Text("Buy ") + Text("Book")
  3. Создать strong и перенести в него хвост Book
Получить две части и прочитать возвращённый текстовый узелСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>ABCD</label>');
$label = $document->documentElement;
$first = $label->firstChild;
$tail = $first->splitText(2);
echo 'Первый Text: [', $first->data, "]\n";
echo 'Возвращённый Text: [', $tail->data, "]\n";
echo 'Текст элемента: ', $label->textContent, "\n";
echo 'Текстовых детей: ', $label->childNodes->length, "\n";
Ожидаемый результат
Первый Text: [AB]
Возвращённый Text: [CD]
Текст элемента: ABCD
Текстовых детей: 2

Аргумент2 —граница перед C. First остался исходным объектом, но теперь содержит AB. Return попал в tail: это новый Text CD, уже вставленный справа от first. Число2 измеряет число детей, а не размер текста. Снаружи label по-прежнему показывает ABCD; чтобы появился видимый тег выделения, дальше хвост переносится под strong.

Обернуть хвост текста в элемент strongСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<description>Buy Book</description>');
$description = $document->documentElement;
$tail = $description->firstChild->splitText(4);
$strong = $document->createElement('strong');
$description->insertBefore($strong, $tail);
$strong->appendChild($tail);
echo $document->saveXML($description), "
";
Ожидаемый результат
<description>Buy <strong>Book</strong></description>

appendChild переносит хвост под strong. Никакая строка с HTML не исполняется: вся структура создана явными узлами.

Связанные операции: DOMCharacterData::substringData, DOMNode::normalize.

Dom\CharacterData::appendDataДополнить текст существующего узла, не создавать рядом новый Text и не менять вложенные элементы родителя.

PHP 8.4+ · dom

Dom\CharacterData::appendData(string $data): void

Дополнить текст существующего узла, не создавать рядом новый Text и не менять вложенные элементы родителя.

CreateFromString получает XML-строку и возвращает document. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. К приветствию Привет нужно дописать имя Анна. AppendData получает только добавляемую строку и меняет тот же Text. Метод возвращает void: результат читается в изменённом data, вызов не присваивают обратно text. Первый пример показывает итоговую строку и одного ребёнка вместо двух; следующий сохраняет имя с амперсандом как корректный XML.

Аргумент / поведениеПолный контракт
$dataСтрока UTF-8, которая дописывается в конец текущего CharacterData. XML-разметка внутри строки остаётся обычным текстом.
Возвратvoid в современном API; возвращённое значение не используется.
Границы / выборНе заменяет appendChild: тот добавляет другой узел. Для полной замены содержимого одного Text присвойте data. Для полной замены всех детей элемента свойство textContent меняет большее поддерево.
Дополнение одного текстового узла
  1. label → Text("Привет")
  2. text.appendData(", Анна")
  3. label → тот же Text("Привет, Анна"); число детей не увеличилось
Дополнить приветствие в одном узлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Привет</label>');
$text = $document->documentElement->firstChild;
$text->appendData(', Анна');
echo $text->data, "
";
echo $document->documentElement->childNodes->length, "
";
Ожидаемый результат
Привет, Анна
1

firstChild — текст внутри label. Метод изменил этот объект; новый сосед не появился.

Добавить название со знаком амперсанда как данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Author: </label>');
$text = $document->documentElement->firstChild;
$text->appendData('A & B');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Author: A &amp; B</label>

В data хранится A & B, а XML-сериализатор корректно экранирует амперсанд. Не подставляйте текст руками в строку с тегами.

Связанные операции: Dom\CharacterData::insertData, Dom\Document::createTextNode.

Dom\CharacterData::substringDataВзять часть текстового поля XML для короткой подписи, сохранив полный исходный текст в документе.

PHP 8.4+ · dom

Dom\CharacterData::substringData(int $offset, int $count): string

Взять часть текстового поля XML для короткой подписи, сохранив полный исходный текст в документе.

CreateFromString получает XML-строку и возвращает document. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. Нужна короткая подпись, а полное значение должно остаться в XML. SubstringData(0,3) начинает с первой кодовой точки и берёт три: П, р, и. Return —отдельная строка При, переданная в echo; исходный data остаётся Привет. Затем preview получает эту строку внутри цикла и добавляет многоточие только при реальном сокращении.

Аргумент / поведениеПолный контракт
$offsetНачальная позиция с нуля. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$countНеотрицательное количество кодовых точек. Если остаток короче, возвращается весь остаток; count=0 даёт пустую строку.
Возвратstring без изменения узла.
Ошибки / границыoffset от 0 до length включительно; выход за диапазон — DOMException с DOM_INDEX_SIZE_ERR. count ограничивается оставшейся длиной. Особенность PHP 8.5.11: отрицательный count в этих методах приводится к unsigned и может обработать весь остаток. Проверяйте count >= 0 сами; не используйте -1 как переносимую договорённость «до конца». Чрезмерные целые вне поддерживаемого диапазона также вызывают INDEX_SIZE_ERR.
Выбор инструментаДля настоящего пользовательского ограничения по видимым символам нужна Unicode-графемная обработка; substringData может разделить составной символ.
Извлечение не меняет источник
  1. Text("Привет"): П0 р1 и2 в3 е4 т5
  2. substringData(0, 3) → "При"
  3. Text в документе по-прежнему "Привет"
Получить первые три кодовые точкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Привет</label>');
$text = $document->documentElement->firstChild;
echo $text->substringData(0, 3), "
";
echo $text->data, "
";
Ожидаемый результат
При
Привет

Вызов возвращает строку; он не удаляет остаток. Номера букв не равны байтовым индексам русского UTF-8.

Построить короткие подписи без переписывания XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<labels><label>Notebook</label><label>Pen</label></labels>');
foreach ($document->documentElement->childNodes as $label) {
    $text = $label->firstChild;
    $preview = $text->substringData(0, 4);
    echo $preview, $text->length > 4 ? '...' : '', "
";
}
Ожидаемый результат
Note...
Pen

В этом формате каждый label содержит один Text. При смешанной разметке сначала определите, надо ли объединить textContent, а не слепо работать только с первым ребёнком.

Связанные операции: Dom\CharacterData::deleteData, Dom\CharacterData::replaceData.

Dom\CharacterData::insertDataВставить префикс или уточнение внутрь одного текстового поля, не собирая текст вручную из байтовых substr.

PHP 8.4+ · dom

Dom\CharacterData::insertData(int $offset, string $data): void

Вставить префикс или уточнение внутрь одного текстового поля, не собирая текст вручную из байтовых substr.

CreateFromString получает XML-строку и возвращает document. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. В строке Заказ42 с пробелом перед цифрами нужно поставить №, сохранив цифры. InsertData(6,№) вставляет строку перед кодовой точкой6; нумерация начинается с0, пробел также занимает позицию. Метод возвращает void: результат читается в изменённом data, вызов не присваивают обратно text. Во втором примере позиция0 ставит метку только в name, оставляя price отдельным элементом.

Аргумент / поведениеПолный контракт
$offsetПозиция перед которой вставляется строка; 0 — начало, length — конец. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$dataВставляемая строка UTF-8; исходный хвост сдвигается вправо.
Возвратvoid: данные изменены в узле; не ожидайте true.
Ошибкиoffset вне 0..length — DOMException / DOM_INDEX_SIZE_ERR. Пустая строка ничего не добавляет. Значения из интерфейса пользователя проверяйте до обращения к DOM.
Вставка без удаления
  1. Text("Заказ 42")
  2. insertData(6, "№")
  3. Text("Заказ №42"); исходные цифры сохранены
Добавить знак номера в подписьСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Заказ 42</label>');
$text = $document->documentElement->firstChild;
$text->insertData(6, '№');
echo $text->data, "
";
Ожидаемый результат
Заказ №42

Шесть — количество кодовых точек в «Заказ »: пять букв и пробел. Это не результат strlen русского текста.

Поставить отметку перед текстом, не затронув соседнюю ценуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<item><name>Book</name><price>100</price></item>');
$item = $document->documentElement;
$text = $item->firstElementChild->firstChild;
$text->insertData(0, '[SALE] ');
echo $item->firstElementChild->textContent, "
";
echo $item->lastElementChild->textContent, "
";
Ожидаемый результат
[SALE] Book
100

Изменяется Text внутри name. Присваивание item->textContent уничтожило бы структуру name/price, поэтому здесь оно не подходит.

Связанные операции: Dom\CharacterData::appendData, Dom\CharacterData::replaceData.

Dom\CharacterData::deleteDataУбрать известный служебный префикс из текстового поля, сохранив сам узел и окружающую разметку.

PHP 8.4+ · dom

Dom\CharacterData::deleteData(int $offset, int $count): void

Убрать известный служебный префикс из текстового поля, сохранив сам узел и окружающую разметку.

CreateFromString получает XML-строку и возвращает document. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. Удаление части строки не требует удалять её Text-узел. В AB-CD индекс2 указывает на дефис; deleteData(2,1) убирает одну кодовую точку и оставляет ABCD в data. Метод возвращает void: результат читается в изменённом data, вызов не присваивают обратно text. При обработке названий ниже сначала проверяется DRAFT:, чтобы обычный Pen не потерял первые буквы.

Аргумент / поведениеПолный контракт
$offsetС какой кодовой точки начать удаление, индекс с нуля. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$countСколько кодовых точек удалить. Число больше остатка удаляет до конца; 0 не меняет текст.
Возвратvoid: данные изменены в узле; не ожидайте true.
Ошибки / края диапазонаoffset от 0 до length включительно; выход за диапазон — DOMException с DOM_INDEX_SIZE_ERR. count ограничивается оставшейся длиной. Особенность PHP 8.5.11: отрицательный count в этих методах приводится к unsigned и может обработать весь остаток. Проверяйте count >= 0 сами; не используйте -1 как переносимую договорённость «до конца». Чрезмерные целые вне поддерживаемого диапазона также вызывают INDEX_SIZE_ERR.
ОсторожностьСначала проверьте, что нужный префикс действительно присутствует. Удалить первые семь символов у любого имени — испортить данные, а не очистить их.
Удаление служебного начала
  1. Text("DRAFT: Book")
  2. Проверка starts_with("DRAFT: ")
  3. deleteData(0, 7) → Text("Book")
Удалить середину строкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>AB-CD</label>');
$text = $document->documentElement->firstChild;
$text->deleteData(2, 1);
echo $text->data, "
";
Ожидаемый результат
ABCD

Удаляется одна кодовая точка с индексом 2 — дефис. Остальные части соединяются в том же Text.

Снять только существующую метку черновикаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<names><name>DRAFT: Book</name><name>Pen</name></names>');
foreach ($document->documentElement->childNodes as $name) {
    $text = $name->firstChild;
    if (str_starts_with($text->data, 'DRAFT: ')) {
        $text->deleteData(0, 7);
    }
    echo $text->data, "
";
}
Ожидаемый результат
Book
Pen

Здесь ASCII-префикс имеет одинаковую длину в байтах и кодовых точках. Для другого префикса не подставляйте strlen, если он содержит многобайтовые символы.

Связанные операции: Dom\CharacterData::substringData, Dom\CharacterData::replaceData.

Dom\CharacterData::replaceDataЗаменить выбранный диапазон текста одной операцией: например, обновить известную часть шаблонной подписи.

PHP 8.4+ · dom

Dom\CharacterData::replaceData(int $offset, int $count, string $data): void

Заменить выбранный диапазон текста одной операцией: например, обновить известную часть шаблонной подписи.

CreateFromString получает XML-строку и возвращает document. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. В известной строке Price:100 с пробелом после двоеточия число начинается с позиции7. ReplaceData(7,3,1200) удаляет три кодовые точки100 и вставляет четыре1200. Метод возвращает void: результат читается в изменённом data, вызов не присваивают обратно text. Следующий случай проверяет префикс Status: и вычисляет остаток длины явно, прежде чем записать ready.

Аргумент / поведениеПолный контракт
$offsetНачало заменяемого диапазона с нуля. Здесь offset и count считаются в кодовых точках UTF-8, не в байтах strlen и не в визуальных графемах. Например, буква с отдельным combining-mark занимает две кодовые точки. PHP DOM использует UTF-8; индексы из браузерного JavaScript DOM (UTF-16) нельзя бездумно переносить для emoji.
$countКоличество удаляемых кодовых точек; превышение остатка означает весь остаток. 0 превращает операцию во вставку.
$dataНовая строка произвольной длины; не обязана совпадать по размеру с удалённым диапазоном.
Возвратvoid: данные изменены в узле; не ожидайте true.
Ошибки / границыoffset от 0 до length включительно; выход за диапазон — DOMException с DOM_INDEX_SIZE_ERR. count ограничивается оставшейся длиной. Особенность PHP 8.5.11: отрицательный count в этих методах приводится к unsigned и может обработать весь остаток. Проверяйте count >= 0 сами; не используйте -1 как переносимую договорённость «до конца». Чрезмерные целые вне поддерживаемого диапазона также вызывают INDEX_SIZE_ERR.
Замена диапазона
  1. Text("Price: 100")
  2. replaceData(7, 3, "1200")
  3. Text("Price: 1200"); новая длина может отличаться
Заменить число в заранее известной подписиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Price: 100</label>');
$text = $document->documentElement->firstChild;
$text->replaceData(7, 3, '1200');
echo $text->data, "
";
Ожидаемый результат
Price: 1200

Это учебная фиксированная строка. Денежное значение в реальном формате лучше хранить отдельным price, а подпись строить из проверенных данных.

Обновить остаток после проверенного префиксаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Status: waiting</label>');
$text = $document->documentElement->firstChild;
if (!str_starts_with($text->data, 'Status: ')) {
    throw new RuntimeException('Unexpected label format');
}
$offset = 8;
$text->replaceData($offset, $text->length - $offset, 'ready');
echo $text->data, "
";
Ожидаемый результат
Status: ready

Вместо отрицательного count явно вычислен остаток. Если формат изменится, проверка остановит правку до повреждения текста.

Связанные операции: Dom\CharacterData::insertData, Dom\CharacterData::deleteData.

Dom\Text::splitTextОтделить часть текстового узла, чтобы обернуть её отдельным элементом, например выделить важное слово в описании.

PHP 8.4+ · dom

Dom\Text::splitText(int $offset): Dom\Text

Отделить часть текстового узла, чтобы обернуть её отдельным элементом, например выделить важное слово в описании.

CreateFromString получает XML-строку и возвращает document. DocumentElement —элемент label, а его firstChild —текстовый узел внутри. Data читает строку одного такого узла. Нужно выделить слово Book внутри описания Buy Book. Сначала Text разделяется на начало и хвост; splitText возвращает объект хвоста, а начало остаётся в исходном объекте. После простого опыта с ABCD новый strong получает именно возвращённый tail. Сам splitText тега выделения не создаёт.

Аргумент / поведениеПолный контракт
$offsetГраница разделения с нуля в кодовых точках UTF-8. 0 оставляет первый узел пустым; length создаёт пустой второй Text.
Изменение и возвратИсходный объект сохраняет текст до offset. Возвращается новый Text с хвостом; если исходный имел родителя, хвост вставлен сразу после него. Современный результат Dom\Text.
Ошибки PHP 8.5.11Отрицательный offset вызывает ValueError. Значение больше длины вызывает DOMException / DOM_INDEX_SIZE_ERR. Эти различия важны при переносе между API.
ГраницаОперация не возвращает массив из двух строк и не создаёт выделение сама. Вторую часть можно переместить в новый элемент. normalize позднее может снова склеить соседние Text.
Разделение и выделение
  1. description → Text("Buy Book")
  2. splitText(4): Text("Buy ") + Text("Book")
  3. Создать strong и перенести в него хвост Book
Получить две части и прочитать возвращённый текстовый узелСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>ABCD</label>');
$label = $document->documentElement;
$first = $label->firstChild;
$tail = $first->splitText(2);
echo 'Первый Text: [', $first->data, "]\n";
echo 'Возвращённый Text: [', $tail->data, "]\n";
echo 'Текст элемента: ', $label->textContent, "\n";
echo 'Текстовых детей: ', $label->childNodes->length, "\n";
Ожидаемый результат
Первый Text: [AB]
Возвращённый Text: [CD]
Текст элемента: ABCD
Текстовых детей: 2

Аргумент2 —граница перед C. First остался исходным объектом, но теперь содержит AB. Return попал в tail: это новый Text CD, уже вставленный справа от first. Число2 измеряет число детей, а не размер текста. Снаружи label по-прежнему показывает ABCD; чтобы появился видимый тег выделения, дальше хвост переносится под strong.

Обернуть хвост текста в элемент strongСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<description>Buy Book</description>');
$description = $document->documentElement;
$tail = $description->firstChild->splitText(4);
$strong = $document->createElement('strong');
$description->insertBefore($strong, $tail);
$strong->appendChild($tail);
echo $document->saveXML($description), "
";
Ожидаемый результат
<description>Buy <strong>Book</strong></description>

appendChild переносит хвост под strong. Никакая строка с HTML не исполняется: вся структура создана явными узлами.

Связанные операции: Dom\CharacterData::substringData, Dom\Node::normalize.

DOMText::__constructСоздать буквальный текст без документа, чтобы затем вставить его в XML: символы & и < не должны превращаться в разметку.

PHP 5+ · dom

DOMText::__construct(string $data = "")

Создать буквальный текст без документа, чтобы затем вставить его в XML: символы & и < не должны превращаться в разметку.</p>

Если документ ещё не создан, старый API позволяет подготовить отдельный DOMText. New вызывает конструктор со строкой и возвращает новый объект; это не вызов парсера. Data сразу содержит A & B, а ownerDocument сначала null. После первого включения в документ видна XML-запись. Современный API вместо этого начинает с документа и createTextNode.

Аргумент / поведениеПолный контракт
$data = ""Содержимое UTF-8; отсутствие аргумента создаёт пустой Text. Строка не разбирается как XML.
Результат newНовый DOMText без parentNode и первоначально без ownerDocument. При первой вставке в старый DOM-документ получает владельца; позже для другого документа нужен importNode.
ВыборКогда документ уже есть, createTextNode на нём яснее задаёт владельца. В современном Dom\Text публичного конструктора нет: используйте фабрику документа.
Конструктор и вставка
  1. new DOMText("A & B") → отдельный узел
  2. label.appendChild(text) → узел включён
  3. saveXML → A & B: экранирование делает сериализатор
Подготовить отдельный текст до создания документаСамостоятельный пример
<?php
declare(strict_types=1);

$text = new DOMText('A & B');
echo 'Подготовленные данные: ', $text->data, "\n";
if ($text->ownerDocument === null) {
    echo "Документ ещё не назначен\n";
}
$document = new DOMDocument();
$document->loadXML('<label/>');
$document->documentElement->appendChild($text);
echo 'После вставки: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
Подготовленные данные: A & B
Документ ещё не назначен
После вставки: <label>A &amp; B</label>

New DOMText получает строку и возвращает объект в text. Пока нет владельца, можно прочитать его data; файл и элемент автоматически не возникли. AppendChild назначает владельца при первой вставке, а saveXML экранирует амперсанд. Следующий пример добавляет угловые скобки в исходные данные и также сохраняет их как текст.

Вставить имя поставщика как текстСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<supplier/>');
$name = new DOMText('A & B <partners>');
$document->documentElement->appendChild($name);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<supplier>A &amp; B &lt;partners&gt;</supplier>

partners остаётся частью имени. Не вызывайте htmlspecialchars заранее: DOM сам экранирует текст, а предварительное экранирование может дать двойные сущности.

Связанные операции: DOMDocument::createTextNode, Dom\Document::createTextNode.

DOMText::isWhitespaceInElementContentОтличить форматирующий пробельный Text от узла с буквами, когда обход XML неожиданно встречает переносы строк между элементами.

PHP 5+ · dom

DOMText::isWhitespaceInElementContent(): bool

Отличить форматирующий пробельный Text от узла с буквами, когда обход XML неожиданно встречает переносы строк между элементами.

При обходе красиво оформленного XML между item встречаются Text с отступами. isWhitespaceInElementContent проверяет строку одного Text и возвращает bool, который управляет веткой чтения. Сначала отмечаем пробельный, буквенный и пустой случаи; затем читаем реальные товары A и B, сохраняя исходное дерево.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; bool. В PHP 8.5.11 проверяется пустой/пробельный текст средствами libxml (XML-пробелы: пробел, табуляция, CR, LF).
Два имениisElementContentWhitespace — alias isWhitespaceInElementContent в реализации PHP; это не дополнительная проверка DTD. Не все Unicode-разделители, например NBSP, относятся к XML-пробелам.
Смысл для приложенияtrue не доказывает, что пробел можно удалить: в смешанном содержимом

A B

он важен для читаемости. Для обхода только элементов проще firstElementChild/nextElementSibling.
Пробел в дереве
  1. pretty XML создаёт Text("\n ") между item
  2. Проверка сообщает «только XML-пробелы»
  3. Приложение решает, значимы ли эти пробелы для данного формата
Разобрать пробельный Text без потери значимых данныхСамостоятельный пример
<?php
declare(strict_types=1);

foreach (['Отступ' => " \t\n", 'Название' => 'Book', 'Пустой' => ''] as $label => $value) {
    $text = new DOMText($value);
    if ($text->isWhitespaceInElementContent()) {
        echo $label, ": только XML-пробелы или пустота\n";
    } else {
        echo $label, ': ', $text->data, "\n";
    }
}
Ожидаемый результат
Отступ: только XML-пробелы или пустота
Название: Book
Пустой: только XML-пробелы или пустота

Метод вызывается без аргументов у очередного DOMText. True объединяет два случая: пустой текст и строку только из XML-пробелов. False позволяет прочитать Book как содержимое. Решение ничего не удаляет: пробел между словами может быть важным. Во втором примере пропускаются только отступы при чтении известного списка товаров.

Пропустить форматирующие Text при обработке списка товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog>
  <item>A</item>
  <item>B</item>
</catalog>');
foreach ($document->documentElement->childNodes as $node) {
    if ($node instanceof DOMText && $node->isWhitespaceInElementContent()) {
        continue;
    }
    if ($node instanceof DOMElement) {
        echo $node->textContent, "
";
    }
}
Ожидаемый результат
A
B

В учебном формате пробелы между item — форматирование. Мы пропускаем их при чтении, а не удаляем из исходного документа.

Связанные операции: DOMText::isElementContentWhitespace, DOMNode::hasChildNodes.

DOMText::isElementContentWhitespaceОтличить форматирующий пробельный Text от узла с буквами, когда обход XML неожиданно встречает переносы строк между элементами.

PHP 5.1+ · dom

DOMText::isElementContentWhitespace(): bool

Отличить форматирующий пробельный Text от узла с буквами, когда обход XML неожиданно встречает переносы строк между элементами.

При обходе красиво оформленного XML между item встречаются Text с отступами. isElementContentWhitespace проверяет строку одного Text и возвращает bool, который управляет веткой чтения. Сначала отмечаем пробельный, буквенный и пустой случаи; затем читаем реальные товары A и B, сохраняя исходное дерево.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; bool. В PHP 8.5.11 проверяется пустой/пробельный текст средствами libxml (XML-пробелы: пробел, табуляция, CR, LF).
Два имениisElementContentWhitespace — alias isWhitespaceInElementContent в реализации PHP; это не дополнительная проверка DTD. Не все Unicode-разделители, например NBSP, относятся к XML-пробелам.
Смысл для приложенияtrue не доказывает, что пробел можно удалить: в смешанном содержимом

A B

он важен для читаемости. Для обхода только элементов проще firstElementChild/nextElementSibling.
Пробел в дереве
  1. pretty XML создаёт Text("\n ") между item
  2. Проверка сообщает «только XML-пробелы»
  3. Приложение решает, значимы ли эти пробелы для данного формата
Разобрать пробельный Text без потери значимых данныхСамостоятельный пример
<?php
declare(strict_types=1);

foreach (['Отступ' => " \t\n", 'Название' => 'Book', 'Пустой' => ''] as $label => $value) {
    $text = new DOMText($value);
    if ($text->isElementContentWhitespace()) {
        echo $label, ": только XML-пробелы или пустота\n";
    } else {
        echo $label, ': ', $text->data, "\n";
    }
}
Ожидаемый результат
Отступ: только XML-пробелы или пустота
Название: Book
Пустой: только XML-пробелы или пустота

Метод вызывается без аргументов у очередного DOMText. True объединяет два случая: пустой текст и строку только из XML-пробелов. False позволяет прочитать Book как содержимое. Решение ничего не удаляет: пробел между словами может быть важным. Во втором примере пропускаются только отступы при чтении известного списка товаров.

Пропустить форматирующие Text при обработке списка товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog>
  <item>A</item>
  <item>B</item>
</catalog>');
foreach ($document->documentElement->childNodes as $node) {
    if ($node instanceof DOMText && $node->isElementContentWhitespace()) {
        continue;
    }
    if ($node instanceof DOMElement) {
        echo $node->textContent, "
";
    }
}
Ожидаемый результат
A
B

В учебном формате пробелы между item — форматирование. Мы пропускаем их при чтении, а не удаляем из исходного документа.

Связанные операции: DOMText::isWhitespaceInElementContent, DOMNode::hasChildNodes.

DOMComment::__constructДобавить техническую заметку в XML, которая останется комментарием, а не товаром или текстом поля.

PHP 5+ · dom

DOMComment::__construct(string $data = "")

Добавить техническую заметку в XML, которая останется комментарием, а не товаром или текстом поля.

Комментарий позволяет оставить публичную техническую заметку в выгрузке. New DOMComment(version1) возвращает объект comment; присваивание data меняет его собственную строку на version2. Для появления в XML его ещё нужно вставить. Второй пример ставит комментарий перед товаром и показывает полную запись каталога.

Аргумент / поведениеПолный контракт
$data = ""Текст комментария UTF-8. Не должен содержать запрещённую XML-последовательность --; приложение отвечает за корректный формат комментария.
РезультатНовый DOMComment, первоначально без владельца/родителя. Вставляется как отдельный узел; свойства data доступны через CharacterData.
ОграниченияКомментарий никогда не является контейнером для дочерних элементов. Историческое описание «read only до вставки» не следует понимать как разрешение добавлять детей позже. В PHP 8.5.11 data нового комментария изменяема.
СекретыКомментарии видны получателю XML/HTML и доступны через исходный код страницы. Не оставляйте в них токены, SQL и персональные данные.
Комментарий в выгрузке
  1. catalog → Comment("export version 2") → item
  2. Сериализация выдаёт <!--export version 2-->
  3. Получатель видит заметку; это не скрытое серверное хранилище
Прочитать и изменить данные комментарияСамостоятельный пример
<?php
declare(strict_types=1);

$comment = new DOMComment('version 1');
$comment->data = 'version 2';
echo $comment->data, "
";
Ожидаемый результат
version 2

Комментарий — CharacterData; его текст доступен без дочернего Text.

Добавить безопасную метку форматаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$catalog = $document->documentElement;
$catalog->insertBefore(new DOMComment('format v2'), $catalog->firstChild);
echo $document->saveXML($catalog), "
";
Ожидаемый результат
<catalog><!--format v2--><item>A</item></catalog>

В заметке только публичная версия формата. Для приложения, которое получает XML, комментарий не заменяет атрибут version с документированным контрактом.

Связанные операции: DOMDocument::createComment, Dom\Document::createComment, DOMCharacterData::replaceData.

DOMCdataSection::__constructПредставить текст с угловыми скобками как CDATA в XML-выгрузке, когда формат получателя требует именно такую запись.

PHP 5+ · dom

DOMCdataSection::__construct(string $data)

Представить текст с угловыми скобками как CDATA в XML-выгрузке, когда формат получателя требует именно такую запись.

Получателю XML иногда нужна форма CDATA для текста с угловыми скобками. Конструктор получает обязательную строку и возвращает узел, который appendChild включает в rule. Сериализация показывает границы CDATA, а textContent возвращает исходное условие. Далее сопоставлены две записи одной строки: CDATA и обычный Text.

Аргумент / поведениеПолный контракт
$dataОбязательная строка UTF-8. Для пустой секции передайте явно ""; в PHP 8.5.11 аргумент обязателен, несмотря старую фразу о значении по умолчанию на странице руководства.
РезультатНовый DOMCdataSection, подкласс DOMText. В документе сохраняется запись <![CDATA[...]]>, а textContent возвращает исходный текст.
ГраницыCDATA не делает содержимое безопасным HTML и не разрешает произвольную XML-последовательность ]]> внутри одной секции. Для обычного текста проще createTextNode: сериализатор сам экранирует символы. В современном API фабрика createCDATASection применима к XML; для HTML правила другие.
CDATA — форма записи текста
  1. Строка "A < B & C"</li>
  2. CDATA сохраняет текст без превращения <B> в элемент
  3. При чтении textContent возвращает ту же строку
Вставить буквальное условие в XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<rule/>');
$data = new DOMCdataSection('A < B & C');
$document->documentElement->appendChild($data);
echo $document->saveXML($document->documentElement), "
";
echo $document->documentElement->textContent, "
";
Ожидаемый результат
<rule><![CDATA[A < B & C]]></rule>
A < B & C

CDATA меняет внешнюю запись XML, но не превращает данные в исполняемый код.

Сравнить две формы записи одних буквальных данныхСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<root><first/><second/></root>');
$first = $document->documentElement->firstElementChild;
$second = $first->nextElementSibling;
$first->appendChild(new DOMCdataSection('<b>Book</b>'));
$second->appendChild(new DOMText('<b>Book</b>'));
echo 'CDATA: ', $document->saveXML($first), "\n";
echo 'Text: ', $document->saveXML($second), "\n";
echo 'Прочитано из CDATA: ', $first->textContent, "\n";
echo 'Прочитано из Text: ', $second->textContent, "\n";
Ожидаемый результат
CDATA: <first><![CDATA[<b>Book</b>]]></first>
Text: <second>&lt;b&gt;Book&lt;/b&gt;</second>
Прочитано из CDATA: <b>Book</b>
Прочитано из Text: <b>Book</b>

Первая ветка записывает строку в секцию CDATA, вторая —обычным Text с экранированием угловых скобок. TextContent в обеих возвращает те же буквальные символы. Элемент b от этих данных не появился. CDATA меняет способ XML-записи и сама по себе не делает последующую вставку строки в HTML безопасной.

Связанные операции: DOMDocument::createCDATASection, Dom\Document::createCDATASection, DOMText::__construct.

DOMProcessingInstruction::__constructДобавить инструкцию для программы-получателя XML, например указание локального стиля отображения; PHP сам такую инструкцию не исполняет.

PHP 5+ · dom

DOMProcessingInstruction::__construct(string $name, string $value = "")

Добавить инструкцию для программы-получателя XML, например указание локального стиля отображения; PHP сам такую инструкцию не исполняет.

Processing instruction адресует служебную строку другой программе. New получает target=export и data=version2, возвращает объект instruction; echo читает оба свойства отдельно. Затем insertBefore ставит фиксированную инструкцию стиля на уровне документа перед catalog. PHP DOM только сохраняет эту запись и не запускает её как код.

Аргумент / поведениеПолный контракт
$nameЦель processing instruction — XML-имя, например xml-stylesheet. Не имя элемента. Зарезервированное xml нельзя использовать как замену XML-декларации.
$value = ""Строка данных инструкции. Это не набор DOMAttr: строку с pseudo-attributes читает получатель. Не включайте завершающую последовательность ?> в данные.
Результат / ошибкиНовый DOMProcessingInstruction без parentNode/ownerDocument. Некорректное XML-имя может вызвать DOMException. Для создания в уже известном документе используйте createProcessingInstruction.
Граница доверияУказание href может заставить внешнюю программу загружать ресурс по её правилам. Не формируйте PI из произвольного URL посетителя. Добавление PI с target=php не запускает PHP внутри DOM.
Инструкция адресована читателю XML
  1. PI: target xml-stylesheet, data href="catalog.css"
  2. XML-сериализация сохраняет инструкцию
  3. Решение читать stylesheet принимает другая программа, не PHP DOM
Прочитать цель и данные инструкцииСамостоятельный пример
<?php
declare(strict_types=1);

$instruction = new DOMProcessingInstruction('export', 'version="2"');
echo $instruction->target, "
";
echo $instruction->data, "
";
Ожидаемый результат
export
version="2"

target и data — два свойства; version здесь не DOM-атрибут и не PHP-переменная.

Вставить фиксированную инструкцию перед корнемСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$instruction = new DOMProcessingInstruction('xml-stylesheet', 'type="text/css" href="catalog.css"');
$document->insertBefore($instruction, $document->documentElement);
echo $document->saveXML($instruction), "
";
echo $document->documentElement->nodeName, "
";
Ожидаемый результат
<?xml-stylesheet type="text/css" href="catalog.css"?>
catalog

Инструкция находится на уровне документа перед единственным корнем. Пример не делает запросов к catalog.css.

Связанные операции: DOMDocument::createProcessingInstruction, Dom\Document::createProcessingInstruction.

DOMChildNode::beforeДобавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

PHP 8.0+ · dom

DOMChildNode::before(DOMNode|string ...$nodes): void

Добавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. before вызывается у него и добавляет соседей перед ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. before("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят перед исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->before('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>&lt;note&gt;<item>A</item></catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->before('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>[<badge>new</badge>]<item>A</item></catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: DOMChildNode::after, DOMChildNode::replaceWith, DOMNode::insertBefore.

DOMChildNode::afterДобавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

PHP 8.0+ · dom

DOMChildNode::after(DOMNode|string ...$nodes): void

Добавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. after вызывается у него и добавляет соседей после ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. after("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят после исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->after('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;note&gt;</catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->after('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>[<badge>new</badge>]</catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: DOMChildNode::before, DOMChildNode::replaceWith, DOMNode::insertBefore.

DOMChildNode::replaceWithЗаменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.0+ · dom

DOMChildNode::replaceWith(DOMNode|string ...$nodes): void

Заменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое элемент item с текстом A, новой последовательностью. В первом примере одиночная строка New становится новым Text, а сохранённый node даёт снятые данные. Затем вместо placeholder появляются буквальная подпись Total: и отдельный элемент price100. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Заменить сам узел и прочитать его прежние данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->replaceWith('New');
echo 'Документ после замены: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятые данные: ', $node->textContent, "\n";
Ожидаемый результат
Документ после замены: <catalog>New</catalog>
Снятые данные: A

ReplaceWith вызван у элемент item с текстом A. Строка New стала новым Text на его месте. Return отсутствует; node по-прежнему хранит прежний отсоединённый объект, поэтому его данные можно прочитать для журнала. Это не присваивание внутреннему textContent старого узла: сам тег item исчез из каталога.

Заменить служебный блок готовой структуройСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$price = $document->createElement('price');
$price->textContent = '100';
$node->replaceWith('Total: ', $price);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>Total: <price>100</price></catalog>

Для редактора карточки это способ заменить placeholder. Если нужен тот же элемент с другими детьми, используйте replaceChildren на элементе.

Связанные операции: DOMChildNode::remove, DOMElement::replaceChildren, DOMNode::replaceChild.

DOMChildNode::removeУдалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.0+ · dom

DOMChildNode::remove(): void

Удалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать элемент item с текстом A, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. Первый опыт показывает полный пустой родитель и сохранённые снятые данные. Далее сначала выбираются черновики в обычный массив, затем удаляются именно эти объекты; готовый B сохраняется.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. Отсутствующий родитель в этой реализации — NOT_FOUND_ERR.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Удалить связь и сохранить снятое содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->remove();
echo 'Оставшееся дерево: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятое содержимое: ', $node->textContent, "\n";
Ожидаемый результат
Оставшееся дерево: <catalog/>
Снятое содержимое: A

Remove без аргументов разорвал связь узла с родителем. Void не даёт значения для присваивания, но прежняя переменная node всё ещё содержит объект с данными. Пустой родитель показан как XML. Повторять remove без проверки parentNode на этой реализации нельзя; следующий пример использует безопасную границу.

Выполнить удаление с проверенной границейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item state="draft">A</item><item state="ready">B</item></catalog>');
$remove = [];
foreach ($document->getElementsByTagName('item') as $item) {
    if ($item->getAttribute('state') === 'draft') {
        $remove[] = $item;
    }
}
foreach ($remove as $item) {
    $item->remove();
}
echo $document->documentElement->textContent, "
";
Ожидаемый результат
B

Выборка отделена от мутации: getElementsByTagName живой, а массив remove хранит конкретные объекты.

Связанные операции: DOMChildNode::replaceWith, DOMNode::removeChild.

DOMCharacterData::beforeДобавить подпись или отметку перед текстового узла без поиска parentNode и вычисления индекса.

PHP 8.0+ · dom

DOMCharacterData::before(DOMNode|string ...$nodes): void

Добавить подпись или отметку перед текстового узла без поиска parentNode и вычисления индекса.

В этом примере опорный узел —Text Old. before вызывается у него и добавляет соседей перед ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей текстового узла, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Соседи текстового узла
  1. Родитель содержит исходный текстового узла
  2. before("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят перед исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->before('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>&lt;note&gt;Old</label>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->before('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>[<badge>new</badge>]Old</label>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: DOMCharacterData::after, DOMCharacterData::replaceWith, DOMNode::insertBefore.

DOMCharacterData::afterДобавить подпись или отметку после текстового узла без поиска parentNode и вычисления индекса.

PHP 8.0+ · dom

DOMCharacterData::after(DOMNode|string ...$nodes): void

Добавить подпись или отметку после текстового узла без поиска parentNode и вычисления индекса.

В этом примере опорный узел —Text Old. after вызывается у него и добавляет соседей после ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей текстового узла, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Соседи текстового узла
  1. Родитель содержит исходный текстового узла
  2. after("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят после исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->after('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Old&lt;note&gt;</label>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->after('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Old[<badge>new</badge>]</label>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: DOMCharacterData::before, DOMCharacterData::replaceWith, DOMNode::insertBefore.

DOMCharacterData::replaceWithЗаменить сам текстового узла новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.0+ · dom

DOMCharacterData::replaceWith(DOMNode|string ...$nodes): void

Заменить сам текстового узла новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое Text Old, новой последовательностью. В первом примере одиночная строка New становится новым Text, а сохранённый node даёт снятые данные. Затем вместо placeholder появляются буквальная подпись Total: и отдельный элемент price100. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей текстового узла, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Заменить сам узел и прочитать его прежние данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->replaceWith('New');
echo 'Документ после замены: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятые данные: ', $node->textContent, "\n";
Ожидаемый результат
Документ после замены: <label>New</label>
Снятые данные: Old

ReplaceWith вызван у Text Old. Строка New стала новым Text на его месте. Return отсутствует; node по-прежнему хранит прежний отсоединённый объект, поэтому его данные можно прочитать для журнала. Это не присваивание внутреннему textContent старого узла: исходный Text заменён другим Text.

Заменить служебный блок готовой структуройСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$price = $document->createElement('price');
$price->textContent = '100';
$node->replaceWith('Total: ', $price);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Total: <price>100</price></label>

Для редактора карточки это способ заменить placeholder. Если нужен тот же элемент с другими детьми, используйте replaceChildren на элементе.

Связанные операции: DOMCharacterData::remove, DOMElement::replaceChildren, DOMNode::replaceChild.

DOMCharacterData::removeУдалить текстового узла из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.0+ · dom

DOMCharacterData::remove(): void

Удалить текстового узла из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать Text Old, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. Первый опыт показывает полный пустой родитель и сохранённые снятые данные. Далее проверка parentNode исключает повторное удаление Text.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. Отсутствующий родитель в этой реализации — NOT_FOUND_ERR.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Удалить связь и сохранить снятое содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->remove();
echo 'Оставшееся дерево: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятое содержимое: ', $node->textContent, "\n";
Ожидаемый результат
Оставшееся дерево: <label/>
Снятое содержимое: Old

Remove без аргументов разорвал связь узла с родителем. Void не даёт значения для присваивания, но прежняя переменная node всё ещё содержит объект с данными. Пустой родитель показан как XML. Повторять remove без проверки parentNode на этой реализации нельзя; следующий пример использует безопасную границу.

Выполнить удаление с проверенной границейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<label>Temporary<price>100</price></label>');
$placeholder = $document->documentElement->firstChild;
$placeholder->remove();
if ($placeholder->parentNode !== null) {
    $placeholder->remove();
}
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label><price>100</price></label>

Проверка parentNode предотвращает повторный remove отсоединённого Text.

Связанные операции: DOMCharacterData::replaceWith, DOMNode::removeChild.

DOMElement::beforeДобавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

PHP 8.0+ · dom

DOMElement::before(DOMNode|string ...$nodes): void

Добавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. before вызывается у него и добавляет соседей перед ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. before("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят перед исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->before('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>&lt;note&gt;<item>A</item></catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->before('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>[<badge>new</badge>]<item>A</item></catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: DOMElement::after, DOMElement::replaceWith, DOMNode::insertBefore.

DOMElement::afterДобавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

PHP 8.0+ · dom

DOMElement::after(DOMNode|string ...$nodes): void

Добавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. after вызывается у него и добавляет соседей после ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. after("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят после исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->after('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;note&gt;</catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->after('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>[<badge>new</badge>]</catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: DOMElement::before, DOMElement::replaceWith, DOMNode::insertBefore.

DOMElement::replaceWithЗаменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.0+ · dom

DOMElement::replaceWith(DOMNode|string ...$nodes): void

Заменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое элемент item с текстом A, новой последовательностью. В первом примере одиночная строка New становится новым Text, а сохранённый node даёт снятые данные. Затем вместо placeholder появляются буквальная подпись Total: и отдельный элемент price100. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Заменить сам узел и прочитать его прежние данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->replaceWith('New');
echo 'Документ после замены: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятые данные: ', $node->textContent, "\n";
Ожидаемый результат
Документ после замены: <catalog>New</catalog>
Снятые данные: A

ReplaceWith вызван у элемент item с текстом A. Строка New стала новым Text на его месте. Return отсутствует; node по-прежнему хранит прежний отсоединённый объект, поэтому его данные можно прочитать для журнала. Это не присваивание внутреннему textContent старого узла: сам тег item исчез из каталога.

Заменить служебный блок готовой структуройСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$price = $document->createElement('price');
$price->textContent = '100';
$node->replaceWith('Total: ', $price);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>Total: <price>100</price></catalog>

Для редактора карточки это способ заменить placeholder. Если нужен тот же элемент с другими детьми, используйте replaceChildren на элементе.

Связанные операции: DOMElement::remove, DOMElement::replaceChildren, DOMNode::replaceChild.

DOMElement::removeУдалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.0+ · dom

DOMElement::remove(): void

Удалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать элемент item с текстом A, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. Первый опыт показывает полный пустой родитель и сохранённые снятые данные. Далее сначала выбираются черновики в обычный массив, затем удаляются именно эти объекты; готовый B сохраняется.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. Отсутствующий родитель в этой реализации — NOT_FOUND_ERR.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Удалить связь и сохранить снятое содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->remove();
echo 'Оставшееся дерево: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятое содержимое: ', $node->textContent, "\n";
Ожидаемый результат
Оставшееся дерево: <catalog/>
Снятое содержимое: A

Remove без аргументов разорвал связь узла с родителем. Void не даёт значения для присваивания, но прежняя переменная node всё ещё содержит объект с данными. Пустой родитель показан как XML. Повторять remove без проверки parentNode на этой реализации нельзя; следующий пример использует безопасную границу.

Выполнить удаление с проверенной границейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item state="draft">A</item><item state="ready">B</item></catalog>');
$remove = [];
foreach ($document->getElementsByTagName('item') as $item) {
    if ($item->getAttribute('state') === 'draft') {
        $remove[] = $item;
    }
}
foreach ($remove as $item) {
    $item->remove();
}
echo $document->documentElement->textContent, "
";
Ожидаемый результат
B

Выборка отделена от мутации: getElementsByTagName живой, а массив remove хранит конкретные объекты.

Связанные операции: DOMElement::replaceWith, DOMNode::removeChild.

Dom\ChildNode::beforeДобавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\ChildNode::before(Dom\Node|string ...$nodes): void

Добавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. before вызывается у него и добавляет соседей перед ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. before("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят перед исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->before('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>&lt;note&gt;<item>A</item></catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->before('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>[<badge>new</badge>]<item>A</item></catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: Dom\ChildNode::after, Dom\ChildNode::replaceWith, Dom\Node::insertBefore.

Dom\ChildNode::afterДобавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\ChildNode::after(Dom\Node|string ...$nodes): void

Добавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. after вызывается у него и добавляет соседей после ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. after("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят после исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->after('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;note&gt;</catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->after('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>[<badge>new</badge>]</catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: Dom\ChildNode::before, Dom\ChildNode::replaceWith, Dom\Node::insertBefore.

Dom\ChildNode::replaceWithЗаменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.4+ · dom

Dom\ChildNode::replaceWith(Dom\Node|string ...$nodes): void

Заменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое элемент item с текстом A, новой последовательностью. В первом примере одиночная строка New становится новым Text, а сохранённый node даёт снятые данные. Затем вместо placeholder появляются буквальная подпись Total: и отдельный элемент price100. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Заменить сам узел и прочитать его прежние данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->replaceWith('New');
echo 'Документ после замены: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятые данные: ', $node->textContent, "\n";
Ожидаемый результат
Документ после замены: <catalog>New</catalog>
Снятые данные: A

ReplaceWith вызван у элемент item с текстом A. Строка New стала новым Text на его месте. Return отсутствует; node по-прежнему хранит прежний отсоединённый объект, поэтому его данные можно прочитать для журнала. Это не присваивание внутреннему textContent старого узла: сам тег item исчез из каталога.

Заменить служебный блок готовой структуройСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$price = $document->createElement('price');
$price->textContent = '100';
$node->replaceWith('Total: ', $price);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>Total: <price>100</price></catalog>

Для редактора карточки это способ заменить placeholder. Если нужен тот же элемент с другими детьми, используйте replaceChildren на элементе.

Связанные операции: Dom\ChildNode::remove, Dom\Element::replaceChildren, Dom\Node::replaceChild.

Dom\ChildNode::removeУдалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.4+ · dom

Dom\ChildNode::remove(): void

Удалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать элемент item с текстом A, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. Первый опыт показывает полный пустой родитель и сохранённые снятые данные. Далее сначала выбираются черновики в обычный массив, затем удаляются именно эти объекты; готовый B сохраняется.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. Отсутствующий родитель в этой реализации — NOT_FOUND_ERR.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Удалить связь и сохранить снятое содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->remove();
echo 'Оставшееся дерево: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятое содержимое: ', $node->textContent, "\n";
Ожидаемый результат
Оставшееся дерево: <catalog/>
Снятое содержимое: A

Remove без аргументов разорвал связь узла с родителем. Void не даёт значения для присваивания, но прежняя переменная node всё ещё содержит объект с данными. Пустой родитель показан как XML. Повторять remove без проверки parentNode на этой реализации нельзя; следующий пример использует безопасную границу.

Выполнить удаление с проверенной границейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item state="draft">A</item><item state="ready">B</item></catalog>');
$remove = [];
foreach ($document->getElementsByTagName('item') as $item) {
    if ($item->getAttribute('state') === 'draft') {
        $remove[] = $item;
    }
}
foreach ($remove as $item) {
    $item->remove();
}
echo $document->documentElement->textContent, "
";
Ожидаемый результат
B

Выборка отделена от мутации: getElementsByTagName живой, а массив remove хранит конкретные объекты.

Связанные операции: Dom\ChildNode::replaceWith, Dom\Node::removeChild.

Dom\CharacterData::beforeДобавить подпись или отметку перед текстового узла без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\CharacterData::before(Dom\Node|string ...$nodes): void

Добавить подпись или отметку перед текстового узла без поиска parentNode и вычисления индекса.

В этом примере опорный узел —Text Old. before вызывается у него и добавляет соседей перед ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей текстового узла, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи текстового узла
  1. Родитель содержит исходный текстового узла
  2. before("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят перед исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->before('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>&lt;note&gt;Old</label>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->before('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>[<badge>new</badge>]Old</label>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: Dom\CharacterData::after, Dom\CharacterData::replaceWith, Dom\Node::insertBefore.

Dom\CharacterData::afterДобавить подпись или отметку после текстового узла без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\CharacterData::after(Dom\Node|string ...$nodes): void

Добавить подпись или отметку после текстового узла без поиска parentNode и вычисления индекса.

В этом примере опорный узел —Text Old. after вызывается у него и добавляет соседей после ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей текстового узла, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи текстового узла
  1. Родитель содержит исходный текстового узла
  2. after("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят после исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->after('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Old&lt;note&gt;</label>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->after('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Old[<badge>new</badge>]</label>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: Dom\CharacterData::before, Dom\CharacterData::replaceWith, Dom\Node::insertBefore.

Dom\CharacterData::replaceWithЗаменить сам текстового узла новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.4+ · dom

Dom\CharacterData::replaceWith(Dom\Node|string ...$nodes): void

Заменить сам текстового узла новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое Text Old, новой последовательностью. В первом примере одиночная строка New становится новым Text, а сохранённый node даёт снятые данные. Затем вместо placeholder появляются буквальная подпись Total: и отдельный элемент price100. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей текстового узла, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Заменить сам узел и прочитать его прежние данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->replaceWith('New');
echo 'Документ после замены: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятые данные: ', $node->textContent, "\n";
Ожидаемый результат
Документ после замены: <label>New</label>
Снятые данные: Old

ReplaceWith вызван у Text Old. Строка New стала новым Text на его месте. Return отсутствует; node по-прежнему хранит прежний отсоединённый объект, поэтому его данные можно прочитать для журнала. Это не присваивание внутреннему textContent старого узла: исходный Text заменён другим Text.

Заменить служебный блок готовой структуройСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$price = $document->createElement('price');
$price->textContent = '100';
$node->replaceWith('Total: ', $price);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label>Total: <price>100</price></label>

Для редактора карточки это способ заменить placeholder. Если нужен тот же элемент с другими детьми, используйте replaceChildren на элементе.

Связанные операции: Dom\CharacterData::remove, Dom\Element::replaceChildren, Dom\Node::replaceChild.

Dom\CharacterData::removeУдалить текстового узла из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.4+ · dom

Dom\CharacterData::remove(): void

Удалить текстового узла из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать Text Old, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. Первый опыт показывает полный пустой родитель и сохранённые снятые данные. Далее проверка parentNode исключает повторное удаление Text.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. Отсутствующий родитель в этой реализации — NOT_FOUND_ERR.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Удалить связь и сохранить снятое содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Old</label>');
$node = $document->documentElement->firstChild;
$node->remove();
echo 'Оставшееся дерево: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятое содержимое: ', $node->textContent, "\n";
Ожидаемый результат
Оставшееся дерево: <label/>
Снятое содержимое: Old

Remove без аргументов разорвал связь узла с родителем. Void не даёт значения для присваивания, но прежняя переменная node всё ещё содержит объект с данными. Пустой родитель показан как XML. Повторять remove без проверки parentNode на этой реализации нельзя; следующий пример использует безопасную границу.

Выполнить удаление с проверенной границейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<label>Temporary<price>100</price></label>');
$placeholder = $document->documentElement->firstChild;
$placeholder->remove();
if ($placeholder->parentNode !== null) {
    $placeholder->remove();
}
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<label><price>100</price></label>

Проверка parentNode предотвращает повторный remove отсоединённого Text.

Связанные операции: Dom\CharacterData::replaceWith, Dom\Node::removeChild.

Dom\Element::beforeДобавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\Element::before(Dom\Node|string ...$nodes): void

Добавить подпись или отметку перед элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. before вызывается у него и добавляет соседей перед ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. before("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят перед исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->before('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>&lt;note&gt;<item>A</item></catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->before('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>[<badge>new</badge>]<item>A</item></catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: Dom\Element::after, Dom\Element::replaceWith, Dom\Node::insertBefore.

Dom\Element::afterДобавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\Element::after(Dom\Node|string ...$nodes): void

Добавить подпись или отметку после элемента без поиска parentNode и вычисления индекса.

В этом примере опорный узел —элемент item с текстом A. after вызывается у него и добавляет соседей после ним, не внутрь него. Первый аргумент-строка с угловыми скобками становится буквальным Text: экранирование видно в XML. Затем отдельно создаётся badge с текстом new и передаётся между строками [ и ]. Порядок этих трёх аргументов сохраняется. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи элемента
  1. Родитель содержит исходный элемента
  2. after("[", badge, "]")
  3. Text("[") → badge → Text("]") стоят после исходного узла, порядок аргументов сохранён
Добавить одного соседа и посмотреть деревоСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->after('<note>');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;note&gt;</catalog>

Вызов сделан у опорного узла. Скобки углового вида переданы как данные и экранированы; метод не является HTML-парсером.

Добавить группу соседей в указанном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$badge = $document->createElement('badge');
$badge->textContent = 'new';
$node->after('[', $badge, ']');
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog><item>A</item>[<badge>new</badge>]</catalog>

Несколько аргументов попадают в дерево последовательно. Элемент badge создаётся отдельно; его текст задаётся через textContent. В обычном приложении подпись выбирается из проверенных бизнес-данных.

Связанные операции: Dom\Element::before, Dom\Element::replaceWith, Dom\Node::insertBefore.

Dom\Element::replaceWithЗаменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.4+ · dom

Dom\Element::replaceWith(Dom\Node|string ...$nodes): void

Заменить сам элемента новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое элемент item с текстом A, новой последовательностью. В первом примере одиночная строка New становится новым Text, а сохранённый node даёт снятые данные. Затем вместо placeholder появляются буквальная подпись Total: и отдельный элемент price100. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстМетод работает на уровне соседей элемента, а не внутри него. Строки превращаются в буквальные Text, не разбираются как XML/HTML.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Заменить сам узел и прочитать его прежние данныеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->replaceWith('New');
echo 'Документ после замены: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятые данные: ', $node->textContent, "\n";
Ожидаемый результат
Документ после замены: <catalog>New</catalog>
Снятые данные: A

ReplaceWith вызван у элемент item с текстом A. Строка New стала новым Text на его месте. Return отсутствует; node по-прежнему хранит прежний отсоединённый объект, поэтому его данные можно прочитать для журнала. Это не присваивание внутреннему textContent старого узла: сам тег item исчез из каталога.

Заменить служебный блок готовой структуройСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$price = $document->createElement('price');
$price->textContent = '100';
$node->replaceWith('Total: ', $price);
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog>Total: <price>100</price></catalog>

Для редактора карточки это способ заменить placeholder. Если нужен тот же элемент с другими детьми, используйте replaceChildren на элементе.

Связанные операции: Dom\Element::remove, Dom\Element::replaceChildren, Dom\Node::replaceChild.

Dom\Element::removeУдалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.4+ · dom

Dom\Element::remove(): void

Удалить элемента из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать элемент item с текстом A, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. Первый опыт показывает полный пустой родитель и сохранённые снятые данные. Далее сначала выбираются черновики в обычный массив, затем удаляются именно эти объекты; готовый B сохраняется.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. Отсутствующий родитель в этой реализации — NOT_FOUND_ERR.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Удалить связь и сохранить снятое содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$node = $document->documentElement->firstChild;
$node->remove();
echo 'Оставшееся дерево: ', $document->saveXML($document->documentElement), "\n";
echo 'Снятое содержимое: ', $node->textContent, "\n";
Ожидаемый результат
Оставшееся дерево: <catalog/>
Снятое содержимое: A

Remove без аргументов разорвал связь узла с родителем. Void не даёт значения для присваивания, но прежняя переменная node всё ещё содержит объект с данными. Пустой родитель показан как XML. Повторять remove без проверки parentNode на этой реализации нельзя; следующий пример использует безопасную границу.

Выполнить удаление с проверенной границейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item state="draft">A</item><item state="ready">B</item></catalog>');
$remove = [];
foreach ($document->getElementsByTagName('item') as $item) {
    if ($item->getAttribute('state') === 'draft') {
        $remove[] = $item;
    }
}
foreach ($remove as $item) {
    $item->remove();
}
echo $document->documentElement->textContent, "
";
Ожидаемый результат
B

Выборка отделена от мутации: getElementsByTagName живой, а массив remove хранит конкретные объекты.

Связанные операции: Dom\Element::replaceWith, Dom\Node::removeChild.

Dom\DocumentType::beforeДобавить подпись или отметку перед декларации типа документа без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\DocumentType::before(Dom\Node|string ...$nodes): void

Добавить подпись или отметку перед декларации типа документа без поиска parentNode и вычисления индекса.

В этом примере опорный узел —DocumentType catalog. before вызывается у него и добавляет соседей перед ним, не внутрь него. Сначала создаём допустимый Comment, затем добавляем группу PI и Comment. Полная последовательность XML показывает их реальное место. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится. SaveXML для DocumentType уже завершает запись переводом строки; дополнительный вывод "\n" даёт видимую пустую строку между частями отчёта.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстУ DocumentType родитель — документ. Здесь допускаются комментарии, PI и корректно расположенный doctype; строка превратится в Text, который современный Document не может принять как непосредственного ребёнка. Нельзя добавлять второй корневой элемент или второй doctype.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи декларации типа документа
  1. Document → DocumentType(catalog) → Element(catalog)
  2. before(Comment): новый сосед на уровне документа
  3. Корень catalog сохраняется; комментарий не является дочерним doctype
Добавить публичную заметку перед декларации типаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$note = $document->createComment('format v2');
$node->before($note);
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
Ожидаемый результат
<!--format v2-->
<!DOCTYPE catalog>

<catalog/>

Node получен из свойства doctype. CreateComment возвращает отдельный узел note; before вставляет его перед doctype на уровне document. Метод возвращает void, поэтому результат читается обходом детей документа. Каждая строка вывода —XML одного ребёнка; catalog остался единственным корневым элементом.

Поставить инструкцию и комментарий в заданном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$instruction = $document->createProcessingInstruction('export', 'version="2"');
$note = $document->createComment('public format');
$node->before($instruction, $note);
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
Ожидаемый результат
<?export version="2"?>
<!--public format-->
<!DOCTYPE catalog>

<catalog/>

Два аргумента вставлены в своём порядке: сначала инструкция export, затем комментарий. Они допустимы рядом с doctype. Обычная строка стала бы Text на уровне документа, а современный XMLDocument не допускает такого ребёнка. Поэтому вариант для элемента или Text нельзя механически переносить к DocumentType.

Связанные операции: Dom\DocumentType::after, Dom\DocumentType::replaceWith, Dom\Node::insertBefore.

Dom\DocumentType::afterДобавить подпись или отметку после декларации типа документа без поиска parentNode и вычисления индекса.

PHP 8.4+ · dom

Dom\DocumentType::after(Dom\Node|string ...$nodes): void

Добавить подпись или отметку после декларации типа документа без поиска parentNode и вычисления индекса.

В этом примере опорный узел —DocumentType catalog. after вызывается у него и добавляет соседей после ним, не внутрь него. Сначала создаём допустимый Comment, затем добавляем группу PI и Comment. Полная последовательность XML показывает их реальное место. Возврат void: повторно читать нужно дерево родителя. Если переместить существующий узел, отдельная копия автоматически не появится. SaveXML для DocumentType уже завершает запись переводом строки; дополнительный вывод "\n" даёт видимую пустую строку между частями отчёта.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько узлов/строк, в порядке аргументов. Многоточие означает variadic: передайте несколько аргументов либо разверните массив через ...$parts. Существующие узлы перемещаются; DocumentFragment отдаёт детей.
Возвратvoid. Результат — изменившийся список детей родителя, а не новый PHP-массив.
Родитель и версияЕсли родителя нет, современный API и старый PHP 8.3+ ничего не делают. В старых PHP 8.0–8.2 такая операция могла бросать HIERARCHY_REQUEST_ERR.
КонтекстУ DocumentType родитель — документ. Здесь допускаются комментарии, PI и корректно расположенный doctype; строка превратится в Text, который современный Document не может принять как непосредственного ребёнка. Нельзя добавлять второй корневой элемент или второй doctype.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Соседи декларации типа документа
  1. Document → DocumentType(catalog) → Element(catalog)
  2. after(Comment): новый сосед на уровне документа
  3. Корень catalog сохраняется; комментарий не является дочерним doctype
Добавить публичную заметку после декларации типаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$note = $document->createComment('format v2');
$node->after($note);
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
Ожидаемый результат
<!DOCTYPE catalog>

<!--format v2-->
<catalog/>

Node получен из свойства doctype. CreateComment возвращает отдельный узел note; after вставляет его после doctype на уровне document. Метод возвращает void, поэтому результат читается обходом детей документа. Каждая строка вывода —XML одного ребёнка; catalog остался единственным корневым элементом.

Поставить инструкцию и комментарий в заданном порядкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$instruction = $document->createProcessingInstruction('export', 'version="2"');
$note = $document->createComment('public format');
$node->after($instruction, $note);
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
Ожидаемый результат
<!DOCTYPE catalog>

<?export version="2"?>
<!--public format-->
<catalog/>

Два аргумента вставлены в своём порядке: сначала инструкция export, затем комментарий. Они допустимы рядом с doctype. Обычная строка стала бы Text на уровне документа, а современный XMLDocument не допускает такого ребёнка. Поэтому вариант для элемента или Text нельзя механически переносить к DocumentType.

Связанные операции: Dom\DocumentType::before, Dom\DocumentType::replaceWith, Dom\Node::insertBefore.

Dom\DocumentType::replaceWithЗаменить сам декларации типа документа новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

PHP 8.4+ · dom

Dom\DocumentType::replaceWith(Dom\Node|string ...$nodes): void

Заменить сам декларации типа документа новой последовательностью узлов; это другое действие, чем заменить его внутреннее содержимое.

Задача —заменить место, занятое DocumentType catalog, новой последовательностью. Для DocumentType сначала разбираем реальный запрет PHP8.5.11 и допустимый путь через replaceChild у документа. Отдельный экспорт без DTD строится в новом дереве. Метод не возвращает снятый узел; нужную переменную сохраняют до вызова. Пустой список аргументов означает удаление. SaveXML для DocumentType уже завершает запись переводом строки; дополнительный вывод "\n" даёт видимую пустую строку между частями отчёта.

Аргумент / поведениеПолный контракт
...$nodesНовые узлы/строки; строки становятся Text, существующие узлы перемещаются. Вызов без аргументов удаляет текущий узел.
Возвратvoid, старый узел не возвращается. Сохранённая переменная всё ещё позволяет читать отсоединённый объект.
РодительБез parentNode операция ничего не делает в современном API и старом PHP 8.3+. Старые PHP 8.0–8.2 могли бросить HIERARCHY_REQUEST_ERR.
КонтекстУ DocumentType родитель — документ. Здесь допускаются комментарии, PI и корректно расположенный doctype; строка превратится в Text, который современный Document не может принять как непосредственного ребёнка. Нельзя добавлять второй корневой элемент или второй doctype.
ОшибкиPHP 8.5.11: replaceWith у DocumentType упирается в NO_MODIFICATION_ALLOWED_ERR. Замена doctype на другой doctype через document->replaceChild показана ниже. Для экспорта без DTD безопаснее собрать новый документ; не используйте замену doctype комментарием как переносимый обход: на проверенной сборке 8.5.11 такая комбинация повредила служебное состояние doctype и завершила процесс аварийно. Dom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Заменяется именно опорный узел
  1. parent → oldNode
  2. oldNode.replaceWith(newNodes...)
  3. parent → новые соседи; oldNode.parentNode = null
Обработать запрет и заменить doctype через документСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$replacement = $document->implementation->createDocumentType('catalog', '', 'catalog-v2.dtd');
try {
    $node->replaceWith($replacement);
} catch (DOMException $error) {
    if ($error->code !== DOM_NO_MODIFICATION_ALLOWED_ERR) {
        throw $error;
    }
    echo "replaceWith: декларация защищена\n";
}
$document->replaceChild($replacement, $node);
echo 'Новая декларация: ', $document->saveXML($document->doctype), "\n";
echo 'Прежняя декларация: ', $document->saveXML($node), "\n";
echo 'Корень: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
replaceWith: декларация защищена
Новая декларация: <!DOCTYPE catalog SYSTEM "catalog-v2.dtd">

Прежняя декларация: <!DOCTYPE catalog>

Корень: <catalog/>

На проверенном PHP8.5.11 replaceWith у doctype отклонён. Ловим только ожидаемый код; другой отказ передаём выше. Затем метод родителя replaceChild выполняет структурную замену. Новый systemId виден в декларации; старая декларация доступна через сохранённый node. Здесь только создаётся ссылка на DTD: файл catalog-v2.dtd не загружается и проверка по DTD не выполняется.

Подготовить отдельную выгрузку без декларации типаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$clean = Dom\XMLDocument::createEmpty();
$clean->appendChild($clean->importNode($document->documentElement, true));
echo "Дети исходного документа:\n";
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
echo "Дети выгрузки:\n";
foreach ($clean->childNodes as $child) {
    echo $clean->saveXML($child), "\n";
}
Ожидаемый результат
Дети исходного документа:
<!DOCTYPE catalog>

<catalog/>
Дети выгрузки:
<catalog/>

CreateEmpty создаёт новый документ. ImportNode(root,true) возвращает глубокую копию корня, appendChild делает её корнем нового дерева. DTD не импортировалась: два вывода показывают точный состав документов. Это экспорт после уже состоявшегося разбора, не средство обезвредить внешние сущности до загрузки XML.

Связанные операции: Dom\DocumentType::remove, Dom\Element::replaceChildren, Dom\Node::replaceChild.

Dom\DocumentType::removeУдалить декларации типа документа из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

PHP 8.4+ · dom

Dom\DocumentType::remove(): void

Удалить декларации типа документа из дерева, имея только сам объект: вызов parentNode->removeChild не требуется.

Нужно убрать DocumentType catalog, сохранив остальное дерево. Вызов remove() делается у самого ребёнка без аргументов, возвращает void и меняет связь с родителем. DocumentType имеет особое ограничение: первый опыт показывает отказ и оставшуюся декларацию; второй использует проверенный метод родителя removeChild. SaveXML для DocumentType уже завершает запись переводом строки; дополнительный вывод "\n" даёт видимую пустую строку между частями отчёта.

Аргумент / поведениеПолный контракт
Аргументы / возвратАргументов нет; void. Удаляется связь с родителем. В PHP 8.5.11 повторный вызов remove на отсоединённом узле бросает DOMException / NOT_FOUND_ERR; проверяйте parentNode !== null. Не переносите браузерное предположение о безусловном no-op.
После удаленияparentNode становится null; сам объект и его данные доступны, пока код хранит переменную. ownerDocument не означает, что узел всё ещё включён в документ.
ОбходЖивые NodeList изменяются вместе с деревом. Сначала собрать массив выбранных объектов, затем удалить — понятный способ не пропустить соседей.
Ошибки / границыЭто не удаление файла и не сохранение XML. Защищённый узел/родитель даёт NO_MODIFICATION_ALLOWED_ERR. У Dom\DocumentType::remove в PHP 8.5.11 эта защита срабатывает и для обычного doctype; документированный метод существует, но для структурного удаления используйте document->removeChild(doctype), как в примере.
Удаление из дерева
  1. node.parentNode → родитель
  2. node.remove() разрывает связь
  3. node остаётся доступен; сохранение документа выполняется отдельно
Увидеть сохранённую декларацию после отклонённого удаленияСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
try {
    $node->remove();
} catch (DOMException $error) {
    if ($error->code !== DOM_NO_MODIFICATION_ALLOWED_ERR) {
        throw $error;
    }
    echo "remove: декларация защищена\n";
}
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
Ожидаемый результат
remove: декларация защищена
<!DOCTYPE catalog>

<catalog/>

На PHP8.5.11 этот remove отказывает с ожидаемым кодом. Полная последовательность детей подтверждает, что doctype остался. Catch не превращает отказ в успешное действие; неожиданное исключение передаётся выше.

Удалить декларацию через родительский документСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog><catalog/>');
$node = $document->doctype;
$removed = $document->removeChild($node);
echo 'Снята декларация: ', $document->saveXML($removed), "\n";
echo "Осталось в документе:\n";
foreach ($document->childNodes as $child) {
    echo $document->saveXML($child), "\n";
}
Ожидаемый результат
Снята декларация: <!DOCTYPE catalog>

Осталось в документе:
<catalog/>

RemoveChild вызывается у родителя и возвращает снятый узел в removed. По нему ещё можно прочитать декларацию для журнала. Обход документа показывает только catalog. Файл на диске этим изменением не переписан: сохранение выполняют отдельно после проверки требуемого формата.

Связанные операции: Dom\DocumentType::replaceWith, Dom\Node::removeChild.

DOMParentNode::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMParentNode::append(DOMNode|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->append('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;new&gt;</catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->append(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>[<item>B</item>]</catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: DOMParentNode::prepend, DOMParentNode::replaceChildren, DOMNode::appendChild.

DOMParentNode::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMParentNode::prepend(DOMNode|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->prepend('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>&lt;new&gt;<item>A</item></catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->prepend(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>[<item>B</item>]<item>A</item></catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: DOMParentNode::append, DOMParentNode::replaceChildren, DOMNode::appendChild.

DOMParentNode::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.3+ · dom

DOMParentNode::replaceChildren(DOMNode|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —элемент catalog с товаром A. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. Первый пример отделяет замену текстом Ready от очистки вызовом без аргументов. Затем цикл создаёт Book и Pen, массив items хранит узлы, а ...$items передаёт их отдельными аргументами. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Заменить содержимое и затем очистить контейнерСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->replaceChildren('Ready');
echo 'После замены: [', $document->saveXML($container), "]\n";
$container->replaceChildren();
echo 'После очистки: [', $document->saveXML($container), "]\n";
Ожидаемый результат
После замены: [<catalog>Ready</catalog>]
После очистки: [<catalog/>]

Первый вызов получает строку Ready, которая становится единственным Text. Второй вызов имеет пустые скобки: ноль аргументов означает ноль новых детей. Сам catalog не заменён и остаётся в документе; исчезли только его дети. Void не присваивается в container: это переменная прежнего объекта.

Подготовить новый набор прежде заменыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$items = [];
foreach (['Book', 'Pen'] as $name) {
    $item = $document->createElement('item');
    $item->textContent = $name;
    $items[] = $item;
}
$container->replaceChildren(...$items);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>Book</item><item>Pen</item></catalog>

Сначала создаются корректные узлы и только затем меняется контейнер. Если проверка исходных бизнес-данных не прошла, можно остановиться до replaceChildren.

Связанные операции: DOMParentNode::append, DOMElement::replaceWith.

DOMDocument::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMDocument::append(DOMNode|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —документ целиком. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Сначала передаём Comment, затем пару Comment и PI: на уровне документа нельзя повторять пример с обычным Text внутри элемента. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстНа Document изменяется список верхнего уровня, где корень является лишь одним из детей. Нормальный XML содержит один корень, допустимые комментарии/PI и правильно расположенный doctype. Это не добавление товара внутрь catalog.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить комментарий в конец документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document;
$container->append($document->createComment('export v2'));
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<catalog><item>A</item></catalog>
<!--export v2-->

Container —сам document. CreateComment сначала возвращает объект, append получает его аргументом и вставляет на верхнем уровне. Return void не присваивается. Обход показывает, что заметка находится рядом с catalog, а не внутри него; товар A сохраняется.

Сохранить порядок двух служебных узлов рядом с корнемСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document;
$note = $document->createComment('public format');
$instruction = $document->createProcessingInstruction('export', 'version="2"');
$container->append($note, $instruction);
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<catalog><item>A</item></catalog>
<!--public format-->
<?export version="2"?>

Аргументы идут note, instruction и в документе остаются в таком порядке. Append поставил всю группу после корня. Комментарий и PI допустимы на этом уровне; обычный Text или второй корневой элемент не подходят современному XMLDocument. Инструкция export не исполняется PHP.

Связанные операции: DOMDocument::prepend, DOMDocument::replaceChildren, DOMNode::appendChild.

DOMDocument::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMDocument::prepend(DOMNode|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —документ целиком. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Сначала передаём Comment, затем пару Comment и PI: на уровне документа нельзя повторять пример с обычным Text внутри элемента. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстНа Document изменяется список верхнего уровня, где корень является лишь одним из детей. Нормальный XML содержит один корень, допустимые комментарии/PI и правильно расположенный doctype. Это не добавление товара внутрь catalog.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить комментарий в начало документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document;
$container->prepend($document->createComment('export v2'));
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<!--export v2-->
<catalog><item>A</item></catalog>

Container —сам document. CreateComment сначала возвращает объект, prepend получает его аргументом и вставляет на верхнем уровне. Return void не присваивается. Обход показывает, что заметка находится рядом с catalog, а не внутри него; товар A сохраняется.

Сохранить порядок двух служебных узлов рядом с корнемСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document;
$note = $document->createComment('public format');
$instruction = $document->createProcessingInstruction('export', 'version="2"');
$container->prepend($note, $instruction);
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<!--public format-->
<?export version="2"?>
<catalog><item>A</item></catalog>

Аргументы идут note, instruction и в документе остаются в таком порядке. Prepend поставил всю группу перед корнем, но не перевернул её. Комментарий и PI допустимы на этом уровне; обычный Text или второй корневой элемент не подходят современному XMLDocument. Инструкция export не исполняется PHP.

Связанные операции: DOMDocument::append, DOMDocument::replaceChildren, DOMNode::appendChild.

DOMDocument::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.3+ · dom

DOMDocument::replaceChildren(DOMNode|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —документ целиком. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. На уровне документа меняется корень и остальные верхние узлы. Первый пример даёт reportReady, второй —полный отчёт с публичным комментарием. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстНа Document изменяется список верхнего уровня, где корень является лишь одним из детей. Нормальный XML содержит один корень, допустимые комментарии/PI и правильно расположенный doctype. Это не добавление товара внутрь catalog.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Получить новый корень отчётаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document;
$replacement = $document->createElement('report');
$replacement->textContent = 'Ready';
$container->replaceChildren($replacement);
echo 'После замены: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
После замены: <report>Ready</report>

Legacy replaceChildren снял прежний catalog и поставил подготовленный report. Переменная container всё ещё указывает на тот же документ. Полный XML нового корня показывает полезный результат. Его текст Ready задан до мутации; файл этим вызовом не сохраняется.

Собрать документ отчёта из заранее подготовленных узловСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document;
$root = $document->createElement('report');
$count = $root->appendChild($document->createElement('count'));
$count->textContent = '2';
$note = $document->createComment('generated locally');
$container->replaceChildren($note, $root);
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<!--generated locally-->
<report><count>2</count></report>

Сначала готовим report с дочерним count2 и отдельную публичную заметку. Только затем передаём note и root в replaceChildren; вывод отражает оба узла. В старом DOM новый набор заменяет прежний catalog целиком. Не удаляйте рабочий документ заранее ради обхода ошибки: отдельный новый документ можно подготовить и проверить до его дальнейшего использования.

Связанные операции: DOMDocument::append, DOMElement::replaceWith.

DOMDocumentFragment::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMDocumentFragment::append(DOMNode|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —временный DocumentFragment с элементом old. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстDocumentFragment — временный контейнер; он может содержать несколько соседних элементов/текстов. При последующей вставке фрагмента в дерево перемещаются его дети, а сам контейнер опустеет.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$container->append('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<old/>&lt;new&gt;

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->append(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<old/>[<item>B</item>]

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: DOMDocumentFragment::prepend, DOMDocumentFragment::replaceChildren, DOMNode::appendChild.

DOMDocumentFragment::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMDocumentFragment::prepend(DOMNode|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —временный DocumentFragment с элементом old. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстDocumentFragment — временный контейнер; он может содержать несколько соседних элементов/текстов. При последующей вставке фрагмента в дерево перемещаются его дети, а сам контейнер опустеет.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$container->prepend('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
&lt;new&gt;<old/>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->prepend(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
[<item>B</item>]<old/>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: DOMDocumentFragment::append, DOMDocumentFragment::replaceChildren, DOMNode::appendChild.

DOMDocumentFragment::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.3+ · dom

DOMDocumentFragment::replaceChildren(DOMNode|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —временный DocumentFragment с элементом old. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. Первый пример отделяет замену текстом Ready от очистки вызовом без аргументов. Затем цикл создаёт Book и Pen, массив items хранит узлы, а ...$items передаёт их отдельными аргументами. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстDocumentFragment — временный контейнер; он может содержать несколько соседних элементов/текстов. При последующей вставке фрагмента в дерево перемещаются его дети, а сам контейнер опустеет.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Заменить содержимое и затем очистить контейнерСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$container->replaceChildren('Ready');
echo 'После замены: [', $document->saveXML($container), "]\n";
$container->replaceChildren();
echo 'После очистки: [', $document->saveXML($container), "]\n";
Ожидаемый результат
После замены: [Ready]
После очистки: []

Первый вызов получает строку Ready, которая становится единственным Text. Второй вызов имеет пустые скобки: ноль аргументов означает ноль новых детей. Fragment не имеет собственного тега, поэтому его пустое содержимое видно между напечатанными скобками []. Void не присваивается в container: это переменная прежнего объекта.

Подготовить новый набор прежде заменыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$items = [];
foreach (['Book', 'Pen'] as $name) {
    $item = $document->createElement('item');
    $item->textContent = $name;
    $items[] = $item;
}
$container->replaceChildren(...$items);
echo $document->saveXML($container), "
";
Ожидаемый результат
<item>Book</item><item>Pen</item>

Сначала создаются корректные узлы и только затем меняется контейнер. Если проверка исходных бизнес-данных не прошла, можно остановиться до replaceChildren.

Связанные операции: DOMDocumentFragment::append, DOMElement::replaceWith.

DOMElement::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMElement::append(DOMNode|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->append('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;new&gt;</catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->append(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>[<item>B</item>]</catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: DOMElement::prepend, DOMElement::replaceChildren, DOMNode::appendChild.

DOMElement::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.0+ · dom

DOMElement::prepend(DOMNode|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Старый DOM местами допускает некорректную структуру документа. Не копируйте примеры со вторым корнем в рабочую XML-выгрузку.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->prepend('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>&lt;new&gt;<item>A</item></catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->prepend(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>[<item>B</item>]<item>A</item></catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: DOMElement::append, DOMElement::replaceChildren, DOMNode::appendChild.

DOMElement::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.3+ · dom

DOMElement::replaceChildren(DOMNode|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —элемент catalog с товаром A. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. Первый пример отделяет замену текстом Ready от очистки вызовом без аргументов. Затем цикл создаёт Book и Pen, массив items хранит узлы, а ...$items передаёт их отдельными аргументами. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDOMException: DOM_HIERARCHY_REQUEST_ERR при цикле/недопустимом типе; DOM_WRONG_DOCUMENT_ERR для узла другого документа; DOM_NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. Обычно strictErrorChecking=true; отключение не превращает ошибку структуры в корректную операцию.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Заменить содержимое и затем очистить контейнерСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->replaceChildren('Ready');
echo 'После замены: [', $document->saveXML($container), "]\n";
$container->replaceChildren();
echo 'После очистки: [', $document->saveXML($container), "]\n";
Ожидаемый результат
После замены: [<catalog>Ready</catalog>]
После очистки: [<catalog/>]

Первый вызов получает строку Ready, которая становится единственным Text. Второй вызов имеет пустые скобки: ноль аргументов означает ноль новых детей. Сам catalog не заменён и остаётся в документе; исчезли только его дети. Void не присваивается в container: это переменная прежнего объекта.

Подготовить новый набор прежде заменыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$items = [];
foreach (['Book', 'Pen'] as $name) {
    $item = $document->createElement('item');
    $item->textContent = $name;
    $items[] = $item;
}
$container->replaceChildren(...$items);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>Book</item><item>Pen</item></catalog>

Сначала создаются корректные узлы и только затем меняется контейнер. Если проверка исходных бизнес-данных не прошла, можно остановиться до replaceChildren.

Связанные операции: DOMElement::append, DOMElement::replaceWith.

Dom\ParentNode::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\ParentNode::append(Dom\Node|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->append('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;new&gt;</catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->append(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>[<item>B</item>]</catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: Dom\ParentNode::prepend, Dom\ParentNode::replaceChildren, Dom\Node::appendChild.

Dom\ParentNode::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\ParentNode::prepend(Dom\Node|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->prepend('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>&lt;new&gt;<item>A</item></catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->prepend(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>[<item>B</item>]<item>A</item></catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: Dom\ParentNode::append, Dom\ParentNode::replaceChildren, Dom\Node::appendChild.

Dom\ParentNode::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.4+ · dom

Dom\ParentNode::replaceChildren(Dom\Node|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —элемент catalog с товаром A. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. Первый пример отделяет замену текстом Ready от очистки вызовом без аргументов. Затем цикл создаёт Book и Pen, массив items хранит узлы, а ...$items передаёт их отдельными аргументами. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Заменить содержимое и затем очистить контейнерСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->replaceChildren('Ready');
echo 'После замены: [', $document->saveXML($container), "]\n";
$container->replaceChildren();
echo 'После очистки: [', $document->saveXML($container), "]\n";
Ожидаемый результат
После замены: [<catalog>Ready</catalog>]
После очистки: [<catalog/>]

Первый вызов получает строку Ready, которая становится единственным Text. Второй вызов имеет пустые скобки: ноль аргументов означает ноль новых детей. Сам catalog не заменён и остаётся в документе; исчезли только его дети. Void не присваивается в container: это переменная прежнего объекта.

Подготовить новый набор прежде заменыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$items = [];
foreach (['Book', 'Pen'] as $name) {
    $item = $document->createElement('item');
    $item->textContent = $name;
    $items[] = $item;
}
$container->replaceChildren(...$items);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>Book</item><item>Pen</item></catalog>

Сначала создаются корректные узлы и только затем меняется контейнер. Если проверка исходных бизнес-данных не прошла, можно остановиться до replaceChildren.

Связанные операции: Dom\ParentNode::append, Dom\Element::replaceWith.

Dom\Document::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\Document::append(Dom\Node|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —документ целиком. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Сначала передаём Comment, затем пару Comment и PI: на уровне документа нельзя повторять пример с обычным Text внутри элемента. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстНа Document изменяется список верхнего уровня, где корень является лишь одним из детей. Нормальный XML содержит один корень, допустимые комментарии/PI и правильно расположенный doctype. Это не добавление товара внутрь catalog.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить комментарий в конец документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document;
$container->append($document->createComment('export v2'));
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<catalog><item>A</item></catalog>
<!--export v2-->

Container —сам document. CreateComment сначала возвращает объект, append получает его аргументом и вставляет на верхнем уровне. Return void не присваивается. Обход показывает, что заметка находится рядом с catalog, а не внутри него; товар A сохраняется.

Сохранить порядок двух служебных узлов рядом с корнемСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document;
$note = $document->createComment('public format');
$instruction = $document->createProcessingInstruction('export', 'version="2"');
$container->append($note, $instruction);
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<catalog><item>A</item></catalog>
<!--public format-->
<?export version="2"?>

Аргументы идут note, instruction и в документе остаются в таком порядке. Append поставил всю группу после корня. Комментарий и PI допустимы на этом уровне; обычный Text или второй корневой элемент не подходят современному XMLDocument. Инструкция export не исполняется PHP.

Связанные операции: Dom\Document::prepend, Dom\Document::replaceChildren, Dom\Node::appendChild.

Dom\Document::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\Document::prepend(Dom\Node|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —документ целиком. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Сначала передаём Comment, затем пару Comment и PI: на уровне документа нельзя повторять пример с обычным Text внутри элемента. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстНа Document изменяется список верхнего уровня, где корень является лишь одним из детей. Нормальный XML содержит один корень, допустимые комментарии/PI и правильно расположенный doctype. Это не добавление товара внутрь catalog.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить комментарий в начало документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document;
$container->prepend($document->createComment('export v2'));
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<!--export v2-->
<catalog><item>A</item></catalog>

Container —сам document. CreateComment сначала возвращает объект, prepend получает его аргументом и вставляет на верхнем уровне. Return void не присваивается. Обход показывает, что заметка находится рядом с catalog, а не внутри него; товар A сохраняется.

Сохранить порядок двух служебных узлов рядом с корнемСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document;
$note = $document->createComment('public format');
$instruction = $document->createProcessingInstruction('export', 'version="2"');
$container->prepend($note, $instruction);
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<!--public format-->
<?export version="2"?>
<catalog><item>A</item></catalog>

Аргументы идут note, instruction и в документе остаются в таком порядке. Prepend поставил всю группу перед корнем, но не перевернул её. Комментарий и PI допустимы на этом уровне; обычный Text или второй корневой элемент не подходят современному XMLDocument. Инструкция export не исполняется PHP.

Связанные операции: Dom\Document::append, Dom\Document::replaceChildren, Dom\Node::appendChild.

Dom\Document::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.4+ · dom

Dom\Document::replaceChildren(Dom\Node|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —документ целиком. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. На уровне документа меняется корень и остальные верхние узлы. Для современного PHP8.5.11 разбираем отказ при уже существующем root, затем рабочую замену через replaceChild и отдельную сборку пустого документа. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстНа Document изменяется список верхнего уровня, где корень является лишь одним из детей. Нормальный XML содержит один корень, допустимые комментарии/PI и правильно расположенный doctype. Это не добавление товара внутрь catalog.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия. В PHP 8.5.11 replaceChildren на современном Document с существующим корнем отклоняет другой корень как второй элемент ещё до удаления старого. Для замены одного корня используйте replaceChild; для полной перестройки создайте отдельный новый документ. Ниже показаны отказ и успешная сборка пустого документа.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Получить новый корень отчётаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document;
$replacement = $document->createElement('report');
$replacement->textContent = 'Ready';
try {
    $container->replaceChildren($replacement);
} catch (DOMException $error) {
    if ($error->code !== DOM_HIERARCHY_REQUEST_ERR) {
        throw $error;
    }
    echo 'ReplaceChildren отклонён; прежний корень: ', $document->saveXML($document->documentElement), "\n";
}
$container->replaceChild($replacement, $container->documentElement);
echo 'После замены: ', $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
ReplaceChildren отклонён; прежний корень: <catalog><item>A</item></catalog>
После замены: <report>Ready</report>

В PHP8.5.11 современный replaceChildren проверяет новый root до снятия прежнего и отклоняет его как второй. После ожидаемого отказа показываем прежний catalog, затем применяем replaceChild для замены именно корня. Полный XML нового корня показывает полезный результат. Его текст Ready задан до мутации; файл этим вызовом не сохраняется.

Собрать документ отчёта из заранее подготовленных узловСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createEmpty();
$container = $document;
$root = $document->createElement('report');
$count = $root->appendChild($document->createElement('count'));
$count->textContent = '2';
$note = $document->createComment('generated locally');
$container->replaceChildren($note, $root);
foreach ($document->childNodes as $node) {
    echo $document->saveXML($node), "\n";
}
Ожидаемый результат
<!--generated locally-->
<report><count>2</count></report>

Сначала готовим report с дочерним count2 и отдельную публичную заметку. Только затем передаём note и root в replaceChildren; вывод отражает оба узла. Современный документ здесь изначально пуст, поэтому конфликт со старым root отсутствует. Не удаляйте рабочий документ заранее ради обхода ошибки: отдельный новый документ можно подготовить и проверить до его дальнейшего использования.

Связанные операции: Dom\Document::append, Dom\Element::replaceWith.

Dom\DocumentFragment::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\DocumentFragment::append(Dom\Node|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —временный DocumentFragment с элементом old. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстDocumentFragment — временный контейнер; он может содержать несколько соседних элементов/текстов. При последующей вставке фрагмента в дерево перемещаются его дети, а сам контейнер опустеет.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$container->append('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<old/>&lt;new&gt;

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->append(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<old/>[<item>B</item>]

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: Dom\DocumentFragment::prepend, Dom\DocumentFragment::replaceChildren, Dom\Node::appendChild.

Dom\DocumentFragment::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\DocumentFragment::prepend(Dom\Node|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —временный DocumentFragment с элементом old. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстDocumentFragment — временный контейнер; он может содержать несколько соседних элементов/текстов. При последующей вставке фрагмента в дерево перемещаются его дети, а сам контейнер опустеет.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$container->prepend('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
&lt;new&gt;<old/>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->prepend(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
[<item>B</item>]<old/>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: Dom\DocumentFragment::append, Dom\DocumentFragment::replaceChildren, Dom\Node::appendChild.

Dom\DocumentFragment::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.4+ · dom

Dom\DocumentFragment::replaceChildren(Dom\Node|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —временный DocumentFragment с элементом old. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. Первый пример отделяет замену текстом Ready от очистки вызовом без аргументов. Затем цикл создаёт Book и Pen, массив items хранит узлы, а ...$items передаёт их отдельными аргументами. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстDocumentFragment — временный контейнер; он может содержать несколько соседних элементов/текстов. При последующей вставке фрагмента в дерево перемещаются его дети, а сам контейнер опустеет.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Заменить содержимое и затем очистить контейнерСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$container->replaceChildren('Ready');
echo 'После замены: [', $document->saveXML($container), "]\n";
$container->replaceChildren();
echo 'После очистки: [', $document->saveXML($container), "]\n";
Ожидаемый результат
После замены: [Ready]
После очистки: []

Первый вызов получает строку Ready, которая становится единственным Text. Второй вызов имеет пустые скобки: ноль аргументов означает ноль новых детей. Fragment не имеет собственного тега, поэтому его пустое содержимое видно между напечатанными скобками []. Void не присваивается в container: это переменная прежнего объекта.

Подготовить новый набор прежде заменыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->createDocumentFragment();
$container->appendChild($document->createElement("old"));
$items = [];
foreach (['Book', 'Pen'] as $name) {
    $item = $document->createElement('item');
    $item->textContent = $name;
    $items[] = $item;
}
$container->replaceChildren(...$items);
echo $document->saveXML($container), "
";
Ожидаемый результат
<item>Book</item><item>Pen</item>

Сначала создаются корректные узлы и только затем меняется контейнер. Если проверка исходных бизнес-данных не прошла, можно остановиться до replaceChildren.

Связанные операции: Dom\DocumentFragment::append, Dom\Element::replaceWith.

Dom\Element::appendДобавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\Element::append(Dom\Node|string ...$nodes): void

Добавить группу детей в конец контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его конец. append получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. append(новые узлы) добавляет в конец
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->append('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>&lt;new&gt;</catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->append(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>A</item>[<item>B</item>]</catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: Dom\Element::prepend, Dom\Element::replaceChildren, Dom\Node::appendChild.

Dom\Element::prependДобавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

PHP 8.4+ · dom

Dom\Element::prepend(Dom\Node|string ...$nodes): void

Добавить группу детей в начало контейнера одним вызовом, сохранив порядок переданных узлов.

Контейнер здесь —элемент catalog с товаром A. Нужно добавить детей в его начало. prepend получает один или несколько узлов/строк и возвращает void. Первая строка с угловыми скобками становится Text и выводится экранированной. Затем создаётся настоящий item B; массив parts хранит строку [, этот узел и строку ]. Запись ...$parts раскрывает три значения в три аргумента метода. Если после вставки нужен новый узел, храните его переменную: return append/prepend его не выдаёт.

Аргумент / поведениеПолный контракт
...$nodesНоль или несколько Node/строк. Строки становятся Text; XML-теги из строки не разбираются. Существующий Node переносится; для копии сначала cloneNode/importNode. Массив передаётся развёрнутым: ...$parts.
Возвратvoid: метод не возвращает добавленный узел. Если он нужен позже, сохраните переменную при createElement; appendChild в отличие от append возвращает узел.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Версии / структураСтарый append/prepend доступны с PHP 8.0; с 8.3 можно вызывать на узле без ownerDocument. Современный DOM строго проверяет структуру Document: например, Text на верхнем уровне или второй root отклоняется.
Меняются дети контейнера
  1. Контейнер → уже существующие дети
  2. prepend(новые узлы) добавляет в начало
  3. Родитель прежний; последовательность аргументов не разворачивается
Добавить одного ребёнка и увидеть место вставкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->prepend('<new>');
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>&lt;new&gt;<item>A</item></catalog>

Угловые скобки в строке остались данными. Для настоящего элемента вызывайте createElement.

Добавить последовательность подготовленных частейСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$item = $document->createElement('item');
$item->textContent = 'B';
$parts = ['[', $item, ']'];
$container->prepend(...$parts);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog>[<item>B</item>]<item>A</item></catalog>

...$parts раскрывает массив в отдельные аргументы. Это синтаксис PHP; он не означает, что DOM хранит PHP-массив как ребёнка.

Связанные операции: Dom\Element::append, Dom\Element::replaceChildren, Dom\Node::appendChild.

Dom\Element::replaceChildrenЗаменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

PHP 8.4+ · dom

Dom\Element::replaceChildren(Dom\Node|string ...$nodes): void

Заменить содержимое контейнера готовой последовательностью, сохранив сам контейнер и не удаляя детей по одному.

Контейнер —элемент catalog с товаром A. Его детей нужно заменить заранее подготовленным набором; сам container остаётся прежним объектом. Первый пример отделяет замену текстом Ready от очистки вызовом без аргументов. Затем цикл создаёт Book и Pen, массив items хранит узлы, а ...$items передаёт их отдельными аргументами. Возврат void означает, что читать результат надо из дерева; ранее сохранённые старые дети сами новыми не становятся.

Аргумент / поведениеПолный контракт
...$nodesВсе новые дети в указанном порядке: объекты или строки, которые станут Text. Без аргументов — полная очистка детей. DocumentFragment отдаёт своих детей.
Возврат / идентичностьvoid; существующий контейнер остаётся тем же объектом. Старые дети отсоединяются, а ранее сохранённые ссылки на них не становятся новыми узлами.
КонтекстМеняются дети контейнера; сам элемент с именем и атрибутами остаётся на месте. Для изменения его соседей нужны before/after.
ОшибкиDom\Exception / DOMException: HIERARCHY_REQUEST_ERR при цикле/недопустимом типе или структуре документа. Современный DOM принимает узел другого современного документа и автоматически усыновляет его; это не копия.
Подготовка / версияPHP 8.3+ в старом DOM, 8.4+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически.
Смена содержимого без смены контейнера
  1. container → old children
  2. replaceChildren(newA, newB)
  3. тот же container → newA, newB; старые дети отсоединены
Заменить содержимое и затем очистить контейнерСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$container->replaceChildren('Ready');
echo 'После замены: [', $document->saveXML($container), "]\n";
$container->replaceChildren();
echo 'После очистки: [', $document->saveXML($container), "]\n";
Ожидаемый результат
После замены: [<catalog>Ready</catalog>]
После очистки: [<catalog/>]

Первый вызов получает строку Ready, которая становится единственным Text. Второй вызов имеет пустые скобки: ноль аргументов означает ноль новых детей. Сам catalog не заменён и остаётся в документе; исчезли только его дети. Void не присваивается в container: это переменная прежнего объекта.

Подготовить новый набор прежде заменыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><item>A</item></catalog>');
$container = $document->documentElement;
$items = [];
foreach (['Book', 'Pen'] as $name) {
    $item = $document->createElement('item');
    $item->textContent = $name;
    $items[] = $item;
}
$container->replaceChildren(...$items);
echo $document->saveXML($container), "
";
Ожидаемый результат
<catalog><item>Book</item><item>Pen</item></catalog>

Сначала создаются корректные узлы и только затем меняется контейнер. Если проверка исходных бизнес-данных не прошла, можно остановиться до replaceChildren.

Связанные операции: Dom\Element::append, Dom\Element::replaceWith.

Dom\TokenList::addДобавить CSS-классы карточке товара без дубликатов и ручного склеивания строки class.

PHP 8.4+ · dom

Dom\TokenList::add(string ...$tokens): void

Добавить CSS-классы карточке товара без дубликатов и ручного склеивания строки class.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. Нужно добавить к card метку featured. Add получает строки отдельными аргументами; повторный card не создаёт дубль. Возврат void: читаем изменённый class исходного элемента. Во втором примере решение о доставке добавляет две метки, затем saveHtml возвращает всю HTML-запись article для echo.

Аргумент / поведениеПолный контракт
...$tokensНоль или несколько непустых строк-токенов. Уже существующий токен повторно не добавляется; новые помещаются в конец набора.
Возврат / эффектvoid. Изменяется связанный атрибут class исходного элемента; TokenList не отдельный несвязанный массив.
ОшибкиValueError при NUL-байте; DOMException с SYNTAX_ERR для пустого токена и INVALID_CHARACTER_ERR для ASCII-пробела, табуляции, LF/CR/form-feed внутри одного токена. Передавайте несколько классов отдельными аргументами, а не "one two". Это правила add/remove/toggle/replace; contains только ищет точный токен.
ГраницаКлассы CSS — метки оформления в HTML. Добавление admin не даёт серверных прав, а удаление hidden не делает данные разрешёнными к выдаче.
Добавление в связанный набор
  1. article class="card"
  2. classList.add("featured", "card")
  3. article class="card featured": дубль card не добавлен
Добавить новый класс и не дублировать старыйСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
$tokens->add('featured', 'card');
echo $element->getAttribute('class'), "
";
Ожидаемый результат
card featured

Объект tokens связан с element. Нет дополнительной операции «сохранить список обратно».

Отметить карточку с доступной доставкойСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
$deliveryAvailable = true;
if ($deliveryAvailable) {
    $tokens->add('delivery-ready', 'highlight');
}
echo $document->saveHtml($element), "
";
Ожидаемый результат
<article class="card delivery-ready highlight"></article>

HTML сериализуется только после принятого бизнес-решения. CSS-класс сообщает о состоянии, а не рассчитывает стоимость доставки.

Связанные операции: Dom\TokenList::remove, Dom\TokenList::toggle.

Dom\TokenList::removeСнять устаревшие CSS-метки, не разбивая и не пересобирая class вручную.

PHP 8.4+ · dom

Dom\TokenList::remove(string ...$tokens): void

Снять устаревшие CSS-метки, не разбивая и не пересобирая class вручную.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. У карточки закончилась подготовка: метки pending или loading больше не нужны. Remove получает точные имена, убирает существующие и игнорирует отсутствующее missing. Void не означает новый список: tokens->value читает текущий связанный class. Во второй операции остаётся базовый card и добавляется ready.

Аргумент / поведениеПолный контракт
...$tokensНоль или несколько точных токенов. Отсутствующие игнорируются; остальные классы сохраняются.
Возвратvoid. Может оставить атрибут class с пустой строкой; это не то же самое, что удалить сам атрибут.
ОшибкиValueError при NUL-байте; DOMException с SYNTAX_ERR для пустого токена и INVALID_CHARACTER_ERR для ASCII-пробела, табуляции, LF/CR/form-feed внутри одного токена. Передавайте несколько классов отдельными аргументами, а не "one two". Это правила add/remove/toggle/replace; contains только ищет точный токен.
АльтернативаЧтобы удалить весь атрибут, используйте Element::removeAttribute("class"). Для приведения одной метки к известному состоянию toggle(token, false) явно выражает намерение.
Удаление конкретных меток
  1. class="card pending busy"
  2. remove("pending", "missing")
  3. class="card busy": неизвестная метка не ошибка
Удалить присутствующий и отсутствующий токеныСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card pending busy');
$tokens = $element->classList;
$tokens->remove('pending', 'missing');
echo $tokens->value, "
";
Ожидаемый результат
card busy

Метод не бросает ошибку из-за отсутствующего missing, но пустой аргумент был бы ошибкой формата токена.

Снять метки загрузки после подготовки готовой карточкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card loading disabled');
$tokens = $element->classList;
$tokens->remove('loading', 'disabled');
$tokens->add('ready');
echo $element->getAttribute('class'), "
";
Ожидаемый результат
card ready

Изменяем только классы, относящиеся к жизненному состоянию компонента; базовый card сохраняется.

Связанные операции: Dom\TokenList::add, Dom\TokenList::toggle, Dom\Element::removeAttribute.

Dom\TokenList::toggleСинхронизировать один CSS-класс с логическим состоянием товара: в наличии или нет, выбран или нет.

PHP 8.4+ · dom

Dom\TokenList::toggle(string $token, ?bool $force = null): bool

Синхронизировать один CSS-класс с логическим состоянием товара: в наличии или нет, выбран или нет.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. Одна метка должна отражать выбор карточки или нулевой остаток. Метод возвращает bool для состояния ПОСЛЕ изменения. Сначала этот return выбирает сообщение и рядом показан HTML. Затем force=($stock===0) каждый раз задаёт нужное состояние; повторный рендер не снимает sold-out случайным переключением.

Аргумент / поведениеПолный контракт
$tokenОдин непустой токен без ASCII-пробелов.
$force = nullnull/отсутствие аргумента меняет наличие на противоположное. true обеспечивает наличие, false — отсутствие. Это bool|null, а не второй токен-класс.
Возвратbool: находится ли токен в наборе ПОСЛЕ вызова. false может означать успешное удаление, а не ошибку.
ОшибкиValueError при NUL-байте; DOMException с SYNTAX_ERR для пустого токена и INVALID_CHARACTER_ERR для ASCII-пробела, табуляции, LF/CR/form-feed внутри одного токена. Передавайте несколько классов отдельными аргументами, а не "one two". Это правила add/remove/toggle/replace; contains только ищет точный токен.
Повторный рендерДля повторяемой генерации HTML используйте force с реальным состоянием. Слепой toggle без force при втором проходе снимет добавленную метку.
Состояние, а не случайное переключение
  1. stock=0 → soldOut=true
  2. toggle("sold-out", true)
  3. Повторный вызов с true оставляет sold-out на месте
Переключить выбор и использовать состояние после вызоваСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
$selected = $tokens->toggle('selected');
echo $selected ? "Карточка выбрана\n" : "Карточка не выбрана\n";
echo $document->saveHtml($element), "\n";
$selected = $tokens->toggle('selected');
echo $selected ? "Карточка выбрана\n" : "Карточка не выбрана\n";
echo $document->saveHtml($element), "\n";
Ожидаемый результат
Карточка выбрана
<article class="card selected"></article>
Карточка не выбрана
<article class="card"></article>

Без force первый toggle добавляет selected и возвращает true в selected. Второй снимает метку и возвращает false. Условие ? : превращает каждое возвращённое состояние в текст, а saveHtml показывает согласованный атрибут. Второй false означает успешное снятие, не ошибку. Метод меняет серверный DOM; браузерная обработка клика сама от него не появляется.

Повторно выставить класс по остатку товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
$stock = 0;
for ($render = 0; $render < 2; $render++) {
    $tokens->toggle('sold-out', $stock === 0);
}
echo $element->getAttribute('class'), "
";
Ожидаемый результат
card sold-out

force делает операцию идемпотентной по отношению к этой метке. Вторая генерация не меняет результат.

Связанные операции: Dom\TokenList::add, Dom\TokenList::remove, Dom\TokenList::contains.

Dom\TokenList::replaceПереименовать существующую CSS-метку, например перейти от старого имени состояния к новому без потери остальных классов.

PHP 8.4+ · dom

Dom\TokenList::replace(string $token, string $newToken): bool

Переименовать существующую CSS-метку, например перейти от старого имени состояния к новому без потери остальных классов.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. При смене имён состояний нужно заменить waiting на pending, сохранив card. Два аргумента означают старую и новую метки. Return сообщает, было ли что заменять; ниже обе ветки показывают фактическую строку class. Следующий пример убирает старое имя при уже существующем новом, не создавая дубль.

Аргумент / поведениеПолный контракт
$tokenТокен, который должен уже присутствовать.
$newTokenЗамена. Если она уже есть, дубликат не создаётся; старый токен убирается.
Возвратtrue, если старый token был в наборе; false, если его не было. При false новый класс не добавляется автоматически.
ОшибкиValueError при NUL-байте; DOMException с SYNTAX_ERR для пустого токена и INVALID_CHARACTER_ERR для ASCII-пробела, табуляции, LF/CR/form-feed внутри одного токена. Передавайте несколько классов отдельными аргументами, а не "one two". Это правила add/remove/toggle/replace; contains только ищет точный токен.
ВыборЕсли задача «класс должен быть», используйте add/toggle(force:true); replace выражает именно миграцию уже существующей метки.
Переименование состояния
  1. class="card waiting"
  2. replace("waiting", "pending")
  3. class="card pending"; true сообщает, что старую метку нашли
Переименовать найденную метку и не создавать лишнююСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card waiting');
$tokens = $element->classList;
if ($tokens->replace('waiting', 'pending')) {
    echo 'После переименования: ', $tokens->value, "\n";
}
if (!$tokens->replace('missing', 'ready')) {
    echo 'Старой missing нет; остаётся: ', $tokens->value, "\n";
}
Ожидаемый результат
После переименования: card pending
Старой missing нет; остаётся: card pending

Первый аргумент waiting найден, поэтому replace меняет его на pending и возвращает true для первой ветки. Missing не найден, return false; ! обращает его для поясняющего сообщения. Ready не добавился. Если цель —безусловно иметь ready, используйте add или toggle с force=true.

Схлопнуть две старые метки в однуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card old-label new-label');
$tokens = $element->classList;
$tokens->replace('old-label', 'new-label');
echo $tokens->value, "
";
echo $tokens->count(), "
";
Ожидаемый результат
card new-label
2

Список является упорядоченным набором: одна и та же метка не дублируется.

Связанные операции: Dom\TokenList::add, Dom\TokenList::toggle.

Dom\TokenList::containsПроверить точное наличие CSS-класса, не получив ложное совпадение по части строки.

PHP 8.4+ · dom

Dom\TokenList::contains(string $token): bool

Проверить точное наличие CSS-класса, не получив ложное совпадение по части строки.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. Нужно проверить точную метку, прежде чем менять HTML. Contains читает набор и возвращает bool без изменения class: card не совпадает с card-large. Первый пример использует проверку для реальной правки data-badge, следующий —для доступной подписи карточки со статусом needs-review.

Аргумент / поведениеПолный контракт
$tokenТочная строка токена, чувствительная к регистру. "card" и "card-large" — разные классы.
Возвратbool без мутации; пустая строка или строка с пробелом не совпадает с отдельным токеном.
ОшибкиВ PHP 8.5.11 NUL в token вызывает ValueError. В отличие от методов изменения, contains не отклоняет пустую строку/ASCII-пробел как DOM-синтаксическую ошибку, а возвращает false.
Альтернативаstr_contains($class, "card") ищет подстроку и может случайно принять card-large. TokenList проверяет границы токенов.
Поиск по токену
  1. class="card-large featured"
  2. contains("card") → false
  3. contains("featured") → true
Не применять правило card к классу card-largeСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card-large featured');
$tokens = $element->classList;
if ($tokens->contains('card')) {
    echo "Применить правила базовой card\n";
} else {
    echo "Класса card нет: card-large отдельная метка\n";
}
if ($tokens->contains('featured')) {
    $element->setAttribute('data-badge', 'Featured');
}
echo $document->saveHtml($element), "\n";
Ожидаемый результат
Класса card нет: card-large отдельная метка
<article class="card-large featured" data-badge="Featured"></article>

Contains возвращает false для card и true для featured. Первое решение не принимает подстроку за отдельный класс; второе ставит атрибут данных для метки. Пустая строка или строка с пробелом также не соответствует одному токену. Эти правила не проверяют, существует ли CSS-описание имени в файле стилей.

Применить правку только к карточке со статусомСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card needs-review');
$tokens = $element->classList;
if ($tokens->contains('needs-review')) {
    $element->setAttribute('aria-label', 'Review required');
}
echo $element->getAttribute('aria-label'), "
";
Ожидаемый результат
Review required

CSS-класс здесь служит меткой уже известного состояния. Политика доступности определяется отдельно, не одним наличием имени класса.

Связанные операции: Dom\TokenList::toggle, Dom\TokenList::supports.

Dom\TokenList::itemПрочитать один CSS-токен по позиции, например показать список меток в инспекторе шаблона.

PHP 8.4+ · dom

Dom\TokenList::item(int $index): ?string

Прочитать один CSS-токен по позиции, например показать список меток в инспекторе шаблона.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. Для инспектора шаблона нужно прочитать имя одного класса по его номеру. Item(0) возвращает первую строку, item(2) —null при двух уникальных метках. Return проверяется до использования. Далее цикл идёт от0 до length-1 и печатает полный нумерованный список.

Аргумент / поведениеПолный контракт
$indexЦелочисленный индекс с 0. Индекс за пределами списка, включая отрицательный, не указывает на токен.
Возврат?string: токен или null при недопустимом индексе. Это не объект DOM и не весь атрибут class.
ПорядокНабор упорядочен по первому появлению токенов; повторения не занимают отдельные индексы. Для проверки известного имени лучше contains; для обхода foreach.
Индекс списка классов
  1. class="card featured card"
  2. Набор: [0]=card, [1]=featured
  3. item(2) → null
Прочитать метку по позиции и обработать отсутствующуюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card featured card');
$tokens = $element->classList;
foreach ([0, 2] as $index) {
    $token = $tokens->item($index);
    if ($token === null) {
        echo 'Позиция ', $index, ': метки нет', "\n";
        continue;
    }
    echo 'Позиция ', $index, ': ', $token, "\n";
}
Ожидаемый результат
Позиция 0: card
Позиция 2: метки нет

Item принимает один индекс и возвращает строку в token либо null. В наборе всего два уникальных имени: повтор card в исходном class не занял третью позицию. После null continue переходит к следующему случаю без попытки использовать отсутствующую строку. Порядок токенов не задаёт приоритет CSS-правил.

Показать нумерованные метки шаблонаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card featured');
$tokens = $element->classList;
for ($index = 0; $index < $tokens->length; $index++) {
    echo $index, ':', $tokens->item($index), "
";
}
Ожидаемый результат
0:card
1:featured

Индекс — технический порядок, не приоритет CSS. Каскад стилей зависит от правил CSS, а не от позиции слова в class.

Связанные операции: Dom\TokenList::count, Dom\TokenList::getIterator, Dom\TokenList::contains.

Dom\TokenList::countПолучить число уникальных классов у элемента, например для диагностического списка меток компонента.

PHP 8.4+ · dom

Dom\TokenList::count(): int

Получить число уникальных классов у элемента, например для диагностического списка меток компонента.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. В отчёте о компоненте нужно назвать количество его уникальных CSS-меток. Метод count() возвращает int; count(tokens) и readonly length читают тот же набор. Первый пример специально сопоставляет три формы записи для исходного повтора card; затем число сопровождается именами до и после внешнего изменения class.

Аргумент / поведениеПолный контракт
Аргументы / возвратНет аргументов; int — число токенов. Совпадает с length и count($tokens), поскольку класс реализует Countable.
ОсобенностьПовторения в исходном class считаются один раз, ASCII-пробелы не создают пустые токены. Число меняется после add/remove и внешнего изменения атрибута.
ГраницаКоличество классов не является оценкой качества/скорости страницы. Не вводите произвольный лимит только из-за большого count.
Счётчик набора
  1. class="card featured card"
  2. Разбор: card, featured
  3. count() = 2, а не число слов с повторами
Сравнить три способа подсчётаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card featured card');
$tokens = $element->classList;
echo $tokens->count(), ':', count($tokens), ':', $tokens->length, "
";
Ожидаемый результат
2:2:2

Все три операции смотрят на один набор токенов.

Показать состав меток после внешней замены атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
echo 'До: ', $tokens->count(), ' метка; ', $tokens->value, "\n";
$element->setAttribute('class', 'card featured selected');
echo 'После: ', $tokens->count(), ' метки; ', $tokens->value, "\n";
Ожидаемый результат
До: 1 метка; card
После: 3 метки; card featured selected

Count возвращает целое, используемое в подписи списка. SetAttribute меняет исходный элемент напрямую; старый объект tokens уже видит новые три имени. Он связан с атрибутом, не является однажды разобранной копией строки. Рядом с числом показан весь состав, поэтому понятно, что именно было посчитано.

Связанные операции: Dom\TokenList::item, Dom\TokenList::getIterator.

Dom\TokenList::getIteratorПередать классы элемента обычному PHP-обходу или получить отдельный массив для безопасного планирования изменений.

PHP 8.4+ · dom

Dom\TokenList::getIterator(): Iterator

Передать классы элемента обычному PHP-обходу или получить отдельный массив для безопасного планирования изменений.

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. Для списка меток нужен foreach. Явный getIterator возвращает Iterator, а цикл получает по очереди index и token: сначала0/card, затем1/featured. Обычный foreach(tokens) вызывает этот механизм сам. Во втором примере строки заранее копируются в snapshot; удаление debug-one не мешает обработать debug-two.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; Iterator по токенам с числовыми ключами. foreach($tokens as ...) вызывает этот механизм автоматически.
Изменение при обходеЭто представление связанного набора. Чтобы удалять выбранные метки без зависимости от изменений индексов, сначала получите массив через iterator_to_array(..., false).
СтоимостьПреобразование в массив создаёт дополнительный список строк. Для простого чтения достаточно foreach без копирования.
Сначала список, потом изменения
  1. getIterator → card, debug-one, debug-two
  2. iterator_to_array фиксирует строки
  3. Удаление debug-* идёт по сохранённому массиву, а не меняющемуся итератору
Обойти классы явно полученным итераторомСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card featured');
$tokens = $element->classList;
foreach ($tokens->getIterator() as $index => $token) {
    echo $index, ':', $token, "
";
}
Ожидаемый результат
0:card
1:featured

В прикладном коде foreach($tokens as $token) короче и делает тот же запрос итератора.

Удалить группу диагностических меток по сохранённому спискуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card debug-one debug-two');
$tokens = $element->classList;
$snapshot = iterator_to_array($tokens->getIterator(), false);
foreach ($snapshot as $token) {
    if (str_starts_with($token, 'debug-')) {
        $tokens->remove($token);
    }
}
echo $tokens->value, "
";
Ожидаемый результат
card

Список snapshot не меняется при remove, поэтому обе диагностические метки будут обработаны.

Связанные операции: Dom\TokenList::remove, Dom\TokenList::count.

Dom\TokenList::supportsОтличить проверку «разрешён ли токен спецификацией атрибута» от обычной проверки «есть ли класс у элемента».

PHP 8.4+ · dom

Dom\TokenList::supports(string $token): bool

Отличить проверку «разрешён ли токен спецификацией атрибута» от обычной проверки «есть ли класс у элемента».

CreateEmpty создаёт HTML-документ, createElement —отдельный article, setAttribute задаёт его строку class. Свойство classList возвращает связанный с этим элементом объект tokens. Не всякая операция общего API применима к конкретному списку. У classList можно хранить имена классов приложения, поэтому supports не имеет фиксированного перечня для ответа. Первый пример обрабатывает TypeError и отдельно читает существующий card. Затем автор приложения сам задаёт allowed и добавляет лишь разрешённый featured.

Аргумент / поведениеПолный контракт
$tokenПроверяемое имя. Метод предназначен для атрибутов с определённым набором допустимых токенов.
Возврат по общему контрактуbool для списков с таким набором: поддерживается токен или нет. Но class допускает пользовательские имена и не определяет supported tokens.
PHP 8.5.11Доступный TokenList используется для classList; supports на нём всегда бросает TypeError. NUL в аргументе раньше отклоняется ValueError. Это не способ узнать, описан ли CSS-класс в таблице стилей.
Рабочая альтернативаДля наличия класса contains. Для правил приложения задайте собственный явный allowlist и in_array(..., true). CSS-файлы DOM при этом не анализирует.
Три разных вопроса
  1. contains("card"): есть в class?
  2. supports("card"): есть нормативный набор токенов атрибута? У class его нет
  3. in_array("card", allowed, true): разрешено ли правилами вашего приложения?
Различить неподходящий вопрос и наличие классаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
try {
    $tokens->supports('card');
} catch (TypeError $error) {
    echo "У class нет заданного стандартом списка разрешённых имён\n";
}
if ($tokens->contains('card')) {
    echo 'Фактически записано: ', $element->getAttribute('class'), "\n";
}
Ожидаемый результат
У class нет заданного стандартом списка разрешённых имён
Фактически записано: card

Supports спрашивает о нормативном перечне допустимых токенов атрибута; для classList такого перечня нет, поэтому PHP8.5.11 бросает TypeError. Это не оценка качества card. Contains задаёт другой вопрос и возвращает true: этот класс действительно записан. Для собственных ограничений следующий пример использует явный массив allowed.

Явно ограничить принимаемые метки компонентаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$element = $document->createElement('article');
$element->setAttribute('class', 'card');
$tokens = $element->classList;
$allowed = ['featured', 'selected'];
foreach (['featured', 'unknown'] as $candidate) {
    if (in_array($candidate, $allowed, true)) {
        $tokens->add($candidate);
    }
}
echo $tokens->value, "
";
Ожидаемый результат
card featured

Это локальная политика компонента, а не HTML-спецификация. Полный HTML-санитайзер требует более широких правил элементов, атрибутов и URL.

Связанные операции: Dom\TokenList::contains, Dom\TokenList::add.

DOMNodeList::itemПолучить один найденный узел, например первую позицию XML-каталога, и явно обработать отсутствие позиции.

PHP 5+ · dom

DOMNodeList::item(int $index): DOMElement|DOMNode|DOMNameSpaceNode|null

Получить один найденный узел, например первую позицию XML-каталога, и явно обработать отсутствие позиции.

GetElementsByTagName(product) возвращает коллекцию найденных элементов. Нужно прочитать первую позицию и не упасть на отсутствующей. Метод принимает индекс и возвращает объект узла либо null; textContent —отдельное чтение данных этого объекта. Второй пример удаляет Tea и показывает, почему тот же индекс0 после мутации уже даёт Coffee.

Аргумент / поведениеПолный контракт
$indexЦелое число с 0; отрицательное или слишком большое значение не обозначает элемент.
ВозвратУзел соответствующего семейства либо null. HTMLCollection содержит только Element, NodeList может содержать Text, Comment и другие узлы.
Живой списокЕсли коллекция получена из childNodes/getElementsByTagName, изменение дерева меняет доступные индексы. XPath/querySelectorAll дают иной, статический список результатов.
Границаitem возвращает объект, а не строку XML. textContent читает текст потомков; сериализация сохраняет разметку.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Получить товар из списка и обработать отсутствующую позициюСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
foreach ([0, 9] as $index) {
    $product = $items->item($index);
    if ($product === null) {
        echo 'Позиция ', $index, ': товара нет', "\n";
        continue;
    }
    echo 'Позиция ', $index, ': ', $product->textContent, "\n";
}
Ожидаемый результат
Позиция 0: Tea
Позиция 9: товара нет

Items —объект списка, а item(index) возвращает один узел в product. Уже у него textContent читает Tea. Позиции начинаются с0. Ветка null обрабатывает отсутствие девятого индекса до доступа к свойству. Нельзя вызывать removeChild у самого списка: для изменения нужен выбранный узел и его родитель.

Снять первую позицию и увидеть сдвиг живого спискаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$first = $items->item(0);
$first->parentNode->removeChild($first);
echo $items->item(0)->textContent, "
";
Ожидаемый результат
Coffee

Индекс 0 после удаления указывает уже на Coffee. Это причина не удалять элементы живой коллекции возрастающим индексом.

Связанные операции: DOMNodeList::count, DOMNodeList::getIterator.

DOMNodeList::countПонять, сколько узлов найдено: есть ли позиции каталога и сколько карточек надо вывести.

PHP 5+ · dom

DOMNodeList::count(): int

Понять, сколько узлов найдено: есть ли позиции каталога и сколько карточек надо вывести.

GetElementsByTagName(product) возвращает коллекцию найденных элементов. Для заголовка каталога нужен размер выборки. Count() без аргументов возвращает int; первый опыт сопоставляет его с readonly length и count(items). Дальше число3 используется вместе с реальным перечнем Tea, Coffee, Cocoa, чтобы было понятно, какие узлы посчитаны.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int, включая 0 для пустого списка. Эквивалентно length и count($items), поскольку реализован Countable.
СтоимостьДля живого результата поиск/подсчёт может обходить дерево; не обещайте O(1) для любого источника коллекции. В неизменяемом цикле можно сохранить размер один раз.
ОтличиеNodeList считает узлы, включая пробельные Text в childNodes. HTMLCollection считает только элементы. Число строк в исходном XML ничего не говорит об этом количестве.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Сопоставить length, count и методСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
echo $items->length, ":", $items->count(), ":", count($items), "
";
Ожидаемый результат
2:2:2

Все способы отражают одну коллекцию.

Добавить позицию и сформировать отчёт по живому спискуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$product = $document->createElement('product');
$product->textContent = 'Cocoa';
$document->documentElement->appendChild($product);
echo 'Товаров: ', $items->count(), "\n";
foreach ($items as $item) {
    echo '- ', $item->textContent, "\n";
}
Ожидаемый результат
Товаров: 3
- Tea
- Coffee
- Cocoa

Count возвращает число3 для заголовка, foreach выводит конкретные три товара. Items получен до добавления Cocoa, но список живой и уже отражает нового ребёнка. Сами строки текста отдельно копируются только при чтении textContent. Для неизменного списка в индексном цикле размер можно сохранить однажды.

Связанные операции: DOMNodeList::item, DOMNodeList::getIterator.

DOMNodeList::getIteratorПеребрать найденные позиции привычным foreach либо зафиксировать набор перед изменениями.

PHP 5+ · dom

DOMNodeList::getIterator(): Iterator

Перебрать найденные позиции привычным foreach либо зафиксировать набор перед изменениями.

GetElementsByTagName(product) возвращает коллекцию найденных элементов. Нужно обработать все найденные позиции. Явный getIterator возвращает Iterator, foreach получает очередной объект item, а textContent даёт его название. Обычный foreach(items) обращается к тому же механизму. Для удаления второй пример сначала фиксирует набор объектов, затем изменяет дерево и выводит отчёт.

Аргумент / поведениеПолный контракт
ВозвратIterator; foreach($items as $node) обращается к этому механизму автоматически. Это обход выбранного списка, а не всего XML независимо от запроса.
ИзмененияДля удаления/перестановки сначала iterator_to_array($items, false): снимок списка ссылок на узлы. Сами узлы не клонируются.
ЦенаСнимок требует дополнительной памяти пропорционально числу ссылок. Для чтения используйте прямой foreach; для огромного файла выбирайте XMLReader.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Получить названия всех позицийСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
foreach ($items->getIterator() as $item) {
    echo $item->textContent, "
";
}
Ожидаемый результат
Tea
Coffee

Вместо явного getIterator в обычной задаче достаточно foreach($items as $item).

Удалить все выбранные товары и сохранить их названия для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$snapshot = iterator_to_array($items->getIterator(), false);
$removedNames = [];
foreach ($snapshot as $item) {
    $removedNames[] = $item->textContent;
    $item->parentNode->removeChild($item);
}
echo 'Сняты: ', implode(', ', $removedNames), "\n";
echo 'Документ: ', $document->saveXml($document->documentElement), "\n";
echo 'Первый сохранённый объект: ', $snapshot[0]->textContent, "\n";
Ожидаемый результат
Сняты: Tea, Coffee
Документ: <catalog/>
Первый сохранённый объект: Tea

GetIterator возвращает обход, iteratorToArray фиксирует ссылки на оба узла в обычном массиве; false назначает последовательные индексы. Удаление Tea из живого items не выкидывает Coffee из snapshot. Последняя строка показывает, что снимок сохранил сам отсоединённый объект Tea, а не глубокую копию всего документа. Для журнала отдельно собраны строки removedNames.

Связанные операции: DOMNodeList::item, DOMNodeList::count.

Dom\NodeList::itemПолучить один найденный узел, например первую позицию XML-каталога, и явно обработать отсутствие позиции.

PHP 8.4+ · dom

Dom\NodeList::item(int $index): ?Dom\Node

Получить один найденный узел, например первую позицию XML-каталога, и явно обработать отсутствие позиции.

Свойство childNodes корневого catalog возвращает список непосредственных детей. В этой строке XML между product нет пробельных Text. Нужно прочитать первую позицию и не упасть на отсутствующей. Метод принимает индекс и возвращает объект узла либо null; textContent —отдельное чтение данных этого объекта. Второй пример удаляет Tea и показывает, почему тот же индекс0 после мутации уже даёт Coffee.

Аргумент / поведениеПолный контракт
$indexЦелое число с 0; отрицательное или слишком большое значение не обозначает элемент.
ВозвратУзел соответствующего семейства либо null. HTMLCollection содержит только Element, NodeList может содержать Text, Comment и другие узлы.
Живой списокЕсли коллекция получена из childNodes/getElementsByTagName, изменение дерева меняет доступные индексы. XPath/querySelectorAll дают иной, статический список результатов.
Границаitem возвращает объект, а не строку XML. textContent читает текст потомков; сериализация сохраняет разметку.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Получить товар из списка и обработать отсутствующую позициюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
foreach ([0, 9] as $index) {
    $product = $items->item($index);
    if ($product === null) {
        echo 'Позиция ', $index, ': товара нет', "\n";
        continue;
    }
    echo 'Позиция ', $index, ': ', $product->textContent, "\n";
}
Ожидаемый результат
Позиция 0: Tea
Позиция 9: товара нет

Items —объект списка, а item(index) возвращает один узел в product. Уже у него textContent читает Tea. Позиции начинаются с0. Ветка null обрабатывает отсутствие девятого индекса до доступа к свойству. Нельзя вызывать removeChild у самого списка: для изменения нужен выбранный узел и его родитель.

Снять первую позицию и увидеть сдвиг живого спискаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
$first = $items->item(0);
$first->parentNode->removeChild($first);
echo $items->item(0)->textContent, "
";
Ожидаемый результат
Coffee

Индекс 0 после удаления указывает уже на Coffee. Это причина не удалять элементы живой коллекции возрастающим индексом.

Связанные операции: Dom\NodeList::count, Dom\NodeList::getIterator.

Dom\NodeList::countПонять, сколько узлов найдено: есть ли позиции каталога и сколько карточек надо вывести.

PHP 8.4+ · dom

Dom\NodeList::count(): int

Понять, сколько узлов найдено: есть ли позиции каталога и сколько карточек надо вывести.

Свойство childNodes корневого catalog возвращает список непосредственных детей. В этой строке XML между product нет пробельных Text. Для заголовка каталога нужен размер выборки. Count() без аргументов возвращает int; первый опыт сопоставляет его с readonly length и count(items). Дальше число3 используется вместе с реальным перечнем Tea, Coffee, Cocoa, чтобы было понятно, какие узлы посчитаны.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int, включая 0 для пустого списка. Эквивалентно length и count($items), поскольку реализован Countable.
СтоимостьДля живого результата поиск/подсчёт может обходить дерево; не обещайте O(1) для любого источника коллекции. В неизменяемом цикле можно сохранить размер один раз.
ОтличиеNodeList считает узлы, включая пробельные Text в childNodes. HTMLCollection считает только элементы. Число строк в исходном XML ничего не говорит об этом количестве.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Сопоставить length, count и методСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
echo $items->length, ":", $items->count(), ":", count($items), "
";
Ожидаемый результат
2:2:2

Все способы отражают одну коллекцию.

Добавить позицию и сформировать отчёт по живому спискуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
$product = $document->createElement('product');
$product->textContent = 'Cocoa';
$document->documentElement->appendChild($product);
echo 'Товаров: ', $items->count(), "\n";
foreach ($items as $item) {
    echo '- ', $item->textContent, "\n";
}
Ожидаемый результат
Товаров: 3
- Tea
- Coffee
- Cocoa

Count возвращает число3 для заголовка, foreach выводит конкретные три товара. Items получен до добавления Cocoa, но список живой и уже отражает нового ребёнка. Сами строки текста отдельно копируются только при чтении textContent. Для неизменного списка в индексном цикле размер можно сохранить однажды.

Связанные операции: Dom\NodeList::item, Dom\NodeList::getIterator.

Dom\NodeList::getIteratorПеребрать найденные позиции привычным foreach либо зафиксировать набор перед изменениями.

PHP 8.4+ · dom

Dom\NodeList::getIterator(): Iterator

Перебрать найденные позиции привычным foreach либо зафиксировать набор перед изменениями.

Свойство childNodes корневого catalog возвращает список непосредственных детей. В этой строке XML между product нет пробельных Text. Нужно обработать все найденные позиции. Явный getIterator возвращает Iterator, foreach получает очередной объект item, а textContent даёт его название. Обычный foreach(items) обращается к тому же механизму. Для удаления второй пример сначала фиксирует набор объектов, затем изменяет дерево и выводит отчёт.

Аргумент / поведениеПолный контракт
ВозвратIterator; foreach($items as $node) обращается к этому механизму автоматически. Это обход выбранного списка, а не всего XML независимо от запроса.
ИзмененияДля удаления/перестановки сначала iterator_to_array($items, false): снимок списка ссылок на узлы. Сами узлы не клонируются.
ЦенаСнимок требует дополнительной памяти пропорционально числу ссылок. Для чтения используйте прямой foreach; для огромного файла выбирайте XMLReader.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Получить названия всех позицийСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
foreach ($items->getIterator() as $item) {
    echo $item->textContent, "
";
}
Ожидаемый результат
Tea
Coffee

Вместо явного getIterator в обычной задаче достаточно foreach($items as $item).

Удалить все выбранные товары и сохранить их названия для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->documentElement->childNodes;
$snapshot = iterator_to_array($items->getIterator(), false);
$removedNames = [];
foreach ($snapshot as $item) {
    $removedNames[] = $item->textContent;
    $item->parentNode->removeChild($item);
}
echo 'Сняты: ', implode(', ', $removedNames), "\n";
echo 'Документ: ', $document->saveXml($document->documentElement), "\n";
echo 'Первый сохранённый объект: ', $snapshot[0]->textContent, "\n";
Ожидаемый результат
Сняты: Tea, Coffee
Документ: <catalog/>
Первый сохранённый объект: Tea

GetIterator возвращает обход, iteratorToArray фиксирует ссылки на оба узла в обычном массиве; false назначает последовательные индексы. Удаление Tea из живого items не выкидывает Coffee из snapshot. Последняя строка показывает, что снимок сохранил сам отсоединённый объект Tea, а не глубокую копию всего документа. Для журнала отдельно собраны строки removedNames.

Связанные операции: Dom\NodeList::item, Dom\NodeList::count.

Dom\HTMLCollection::itemПолучить один найденный узел, например первую позицию XML-каталога, и явно обработать отсутствие позиции.

PHP 8.4+ · dom

Dom\HTMLCollection::item(int $index): ?Dom\Element

Получить один найденный узел, например первую позицию XML-каталога, и явно обработать отсутствие позиции.

GetElementsByTagName(product) возвращает коллекцию найденных элементов. Нужно прочитать первую позицию и не упасть на отсутствующей. Метод принимает индекс и возвращает объект узла либо null; textContent —отдельное чтение данных этого объекта. Второй пример удаляет Tea и показывает, почему тот же индекс0 после мутации уже даёт Coffee.

Аргумент / поведениеПолный контракт
$indexЦелое число с 0; отрицательное или слишком большое значение не обозначает элемент.
ВозвратУзел соответствующего семейства либо null. HTMLCollection содержит только Element, NodeList может содержать Text, Comment и другие узлы.
Живой списокЕсли коллекция получена из childNodes/getElementsByTagName, изменение дерева меняет доступные индексы. XPath/querySelectorAll дают иной, статический список результатов.
Границаitem возвращает объект, а не строку XML. textContent читает текст потомков; сериализация сохраняет разметку.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Получить товар из списка и обработать отсутствующую позициюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
foreach ([0, 9] as $index) {
    $product = $items->item($index);
    if ($product === null) {
        echo 'Позиция ', $index, ': товара нет', "\n";
        continue;
    }
    echo 'Позиция ', $index, ': ', $product->textContent, "\n";
}
Ожидаемый результат
Позиция 0: Tea
Позиция 9: товара нет

Items —объект списка, а item(index) возвращает один узел в product. Уже у него textContent читает Tea. Позиции начинаются с0. Ветка null обрабатывает отсутствие девятого индекса до доступа к свойству. Нельзя вызывать removeChild у самого списка: для изменения нужен выбранный узел и его родитель.

Снять первую позицию и увидеть сдвиг живого спискаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$first = $items->item(0);
$first->parentNode->removeChild($first);
echo $items->item(0)->textContent, "
";
Ожидаемый результат
Coffee

Индекс 0 после удаления указывает уже на Coffee. Это причина не удалять элементы живой коллекции возрастающим индексом.

Связанные операции: Dom\HTMLCollection::count, Dom\HTMLCollection::getIterator.

Dom\HTMLCollection::countПонять, сколько узлов найдено: есть ли позиции каталога и сколько карточек надо вывести.

PHP 8.4+ · dom

Dom\HTMLCollection::count(): int

Понять, сколько узлов найдено: есть ли позиции каталога и сколько карточек надо вывести.

GetElementsByTagName(product) возвращает коллекцию найденных элементов. Для заголовка каталога нужен размер выборки. Count() без аргументов возвращает int; первый опыт сопоставляет его с readonly length и count(items). Дальше число3 используется вместе с реальным перечнем Tea, Coffee, Cocoa, чтобы было понятно, какие узлы посчитаны.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int, включая 0 для пустого списка. Эквивалентно length и count($items), поскольку реализован Countable.
СтоимостьДля живого результата поиск/подсчёт может обходить дерево; не обещайте O(1) для любого источника коллекции. В неизменяемом цикле можно сохранить размер один раз.
ОтличиеNodeList считает узлы, включая пробельные Text в childNodes. HTMLCollection считает только элементы. Число строк в исходном XML ничего не говорит об этом количестве.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Сопоставить length, count и методСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
echo $items->length, ":", $items->count(), ":", count($items), "
";
Ожидаемый результат
2:2:2

Все способы отражают одну коллекцию.

Добавить позицию и сформировать отчёт по живому спискуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$product = $document->createElement('product');
$product->textContent = 'Cocoa';
$document->documentElement->appendChild($product);
echo 'Товаров: ', $items->count(), "\n";
foreach ($items as $item) {
    echo '- ', $item->textContent, "\n";
}
Ожидаемый результат
Товаров: 3
- Tea
- Coffee
- Cocoa

Count возвращает число3 для заголовка, foreach выводит конкретные три товара. Items получен до добавления Cocoa, но список живой и уже отражает нового ребёнка. Сами строки текста отдельно копируются только при чтении textContent. Для неизменного списка в индексном цикле размер можно сохранить однажды.

Связанные операции: Dom\HTMLCollection::item, Dom\HTMLCollection::getIterator.

Dom\HTMLCollection::getIteratorПеребрать найденные позиции привычным foreach либо зафиксировать набор перед изменениями.

PHP 8.4+ · dom

Dom\HTMLCollection::getIterator(): Iterator

Перебрать найденные позиции привычным foreach либо зафиксировать набор перед изменениями.

GetElementsByTagName(product) возвращает коллекцию найденных элементов. Нужно обработать все найденные позиции. Явный getIterator возвращает Iterator, foreach получает очередной объект item, а textContent даёт его название. Обычный foreach(items) обращается к тому же механизму. Для удаления второй пример сначала фиксирует набор объектов, затем изменяет дерево и выводит отчёт.

Аргумент / поведениеПолный контракт
ВозвратIterator; foreach($items as $node) обращается к этому механизму автоматически. Это обход выбранного списка, а не всего XML независимо от запроса.
ИзмененияДля удаления/перестановки сначала iterator_to_array($items, false): снимок списка ссылок на узлы. Сами узлы не клонируются.
ЦенаСнимок требует дополнительной памяти пропорционально числу ссылок. Для чтения используйте прямой foreach; для огромного файла выбирайте XMLReader.
Документ и его представление
  1. catalog → product Tea, product Coffee
  2. items хранит связь с узлами, не их текстовую копию
  3. item(0) → узел Tea; textContent → строка Tea
Получить названия всех позицийСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
foreach ($items->getIterator() as $item) {
    echo $item->textContent, "
";
}
Ожидаемый результат
Tea
Coffee

Вместо явного getIterator в обычной задаче достаточно foreach($items as $item).

Удалить все выбранные товары и сохранить их названия для журналаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product id="p1">Tea</product><product id="p2">Coffee</product></catalog>');
$items = $document->getElementsByTagName("product");
$snapshot = iterator_to_array($items->getIterator(), false);
$removedNames = [];
foreach ($snapshot as $item) {
    $removedNames[] = $item->textContent;
    $item->parentNode->removeChild($item);
}
echo 'Сняты: ', implode(', ', $removedNames), "\n";
echo 'Документ: ', $document->saveXml($document->documentElement), "\n";
echo 'Первый сохранённый объект: ', $snapshot[0]->textContent, "\n";
Ожидаемый результат
Сняты: Tea, Coffee
Документ: <catalog/>
Первый сохранённый объект: Tea

GetIterator возвращает обход, iteratorToArray фиксирует ссылки на оба узла в обычном массиве; false назначает последовательные индексы. Удаление Tea из живого items не выкидывает Coffee из snapshot. Последняя строка показывает, что снимок сохранил сам отсоединённый объект Tea, а не глубокую копию всего документа. Для журнала отдельно собраны строки removedNames.

Связанные операции: Dom\HTMLCollection::item, Dom\HTMLCollection::count.

Dom\HTMLCollection::namedItemНайти элемент HTML-коллекции по id либо name, например поле выгруженной HTML-формы.

PHP 8.4+ · dom

Dom\HTMLCollection::namedItem(string $key): ?Dom\Element

Найти элемент HTML-коллекции по id либо name, например поле выгруженной HTML-формы.

В выгруженном HTML нужно найти конкретное поле среди input. NamedItem принимает строку ключа, ищет первый подходящий id или name и возвращает Element либо null. Первый пример записывает адрес в поле email; второй отдельно показывает поиск по id=order при другом name=number и итоговое значение1042.

Аргумент / поведениеПолный контракт
$keyТочное непустое имя. Пустая строка даёт null.
ВозвратПервый Dom\Element в порядке документа с таким id или name; name учитывается только у элементов HTML-пространства имён. Нет совпадения → null.
ОграничениеПоиск ограничен уже выбранной коллекцией. Это не document.getElementById для всего документа, не CSS-селектор и не массив всех совпадений.
Поиск в выбранных input
  1. items = два поля формы
  2. namedItem("email") сравнивает id/name
  3. Возвращается узел второго input, а не его значение
Найти поле в коллекции и подготовить его значениеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString("<!DOCTYPE html><html><head><title>Form</title></head><body><main><input id=order name=number><input name=email></main></body></html>");
$items = $document->getElementsByTagName("input");
foreach (['email', 'missing'] as $key) {
    $field = $items->namedItem($key);
    if ($field === null) {
        echo 'Поле ', $key, ' отсутствует: шаблон нужно проверить', "\n";
        continue;
    }
    $field->setAttribute('value', 'reader@example.test');
    echo $document->saveHtml($field), "\n";
}
Ожидаемый результат
<input name="email" value="reader@example.test">
Поле missing отсутствует: шаблон нужно проверить

NamedItem возвращает объект input по его name; потом setAttribute записывает value. Поле missing даёт null, поэтому код сообщает о несовпадении шаблона вместо вызова метода у null. Поиск ограничен ранее выбранными input. HTML только подготовлен на сервере; форма здесь не отправляется.

Поставить значение полю, найденному по idСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString("<!DOCTYPE html><html><head><title>Form</title></head><body><main><input id=order name=number><input name=email></main></body></html>");
$items = $document->getElementsByTagName("input");
$field = $items->namedItem("order");
$field->setAttribute("value", "1042");
echo $field->getAttribute("name"), ":", $field->getAttribute("value"), "
";
Ожидаемый результат
number:1042

id order и name number выполняют разные роли. HTMLCollection разрешает найти объект по обоим.

Связанные операции: Dom\HTMLCollection::item, Dom\Document::getElementById.

DOMNamedNodeMap::itemПосмотреть один элемент именованной карты по позиции: атрибут либо объявление из DTD.

PHP 5+ · dom

DOMNamedNodeMap::item(int $index): ?DOMNode

Посмотреть один элемент именованной карты по позиции: атрибут либо объявление из DTD.

Карта содержит атрибуты товара product. Item принимает индекс и возвращает объект записи либо null. Первый пример намеренно оставляет одну запись, чтобы показать цепочку карта→узел→полезное поле. Затем несколько имён собираются по индексам и сортируются для стабильного отчёта; их позиция не становится бизнес-ключом.

Аргумент / поведениеПолный контракт
$indexЦелочисленная позиция с 0; выход за границы даёт null.
ВозвратУзел или null. В карте атрибутов это Attr; DtdNamedNodeMap выдаёт Entity либо Notation в зависимости от выбранного свойства DTD.
ПорядокНе используйте позицию как смысловой ключ: порядок атрибутов/DTD-объявлений не является правилом бизнеса. Для известного имени используйте getNamedItem/GetNamedItemNS.
Позиция против имени
  1. map содержит именованные записи
  2. item(0) выбирает позицию
  3. getNamedItem("id") выбирает смысловой ключ
Получить атрибут из карты и прочитать его имя и значениеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1"/>');
$items = $document->documentElement->attributes;
foreach ([0, 999] as $index) {
    $attribute = $items->item($index);
    if ($attribute === null) {
        echo 'Атрибута на позиции ', $index, ' нет', "\n";
        continue;
    }
    echo $attribute->nodeName, '=', $attribute->nodeValue, "\n";
}
Ожидаемый результат
id=p1
Атрибута на позиции 999 нет

Attributes возвращает карту, item(0) —объект Attr, nodeName/nodeValue —его имя id и строку p1. В минимальном XML всего один атрибут, поэтому порядок не влияет на пример. Для неизвестного результата сначала обрабатывается null. Имя id в рабочем коде надёжнее искать через getNamedItem.

Собрать стабильный список имён независимо от порядкаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$names = [];
for ($index = 0; $index < $items->length; $index++) {
    $names[] = $items->item($index)->nodeName;
}
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
id,p:amount,stock

Сортировка делает отчёт детерминированным; сам DOM-каталог не переставляется.

Связанные операции: DOMNamedNodeMap::getNamedItem, DOMNamedNodeMap::count.

DOMNamedNodeMap::getNamedItemПрочитать именованный узел карты: например атрибут id товара или сущность DTD.

PHP 5+ · dom

DOMNamedNodeMap::getNamedItem(string $qualifiedName): ?DOMNode

Прочитать именованный узел карты: например атрибут id товара или сущность DTD.

Нужно читать атрибут id товара независимо от порядка карты. Аргумент —строка имени, return —узел, а не его значение. Простой пример читает nodeValue=p1; следующий различает p1, пустой note и отсутствующий missing. Для имени с префиксом, которое может меняться между поставщиками, используется пара URI/localName в getNamedItemNS.

Аргумент / поведениеПолный контракт
$qualifiedNameСтроковое квалифицированное имя, включая префикс при наличии. Для независимости от выбранного префикса используйте getNamedItemNS.
ВозвратСоответствующий узел либо null при отсутствии. Не путайте null с существующим атрибутом, значение которого пустая строка.
МутацияПоиск не создаёт атрибут. Изменение найденного Attr.value изменяет тот же элемент; создание/удаление делается методами Element.
Из имени к объекту
  1. map.getNamedItem("id")
  2. Объект с nodeName=id
  3. У Attr.value содержится значение; у современной Entity nodeValue равен null. null вместо объекта означает отсутствие узла
Получить имя сущности либо значение атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$node = $items->getNamedItem("id");
echo $node->nodeValue, "
";
Ожидаемый результат
p1

Здесь имя известно заранее. Для неизвестного имени сначала проверяйте null.

Отличить пустое значение атрибута от отсутствующего поляСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" note=""/>');
$items = $document->documentElement->attributes;
foreach (['id', 'note', 'missing'] as $name) {
    $node = $items->getNamedItem($name);
    if ($node === null) {
        echo $name, ': поле отсутствует', "\n";
    } else {
        echo $name, ': [', $node->nodeValue, "]\n";
    }
}
Ожидаемый результат
id: [p1]
note: []
missing: поле отсутствует

Return у id и note —объект Attr, даже если note содержит пустую строку. Квадратные скобки делают её видимой. Только отсутствующий missing возвращает null. Это важно для формы импорта: пустое замечание и не переданное поле могут иметь разные правила обновления.

Связанные операции: DOMNamedNodeMap::getNamedItemNS, DOMNamedNodeMap::item.

DOMNamedNodeMap::getNamedItemNSИскать именованный узел по пространству имён и локальному имени, независимо от текстового префикса.

PHP 5+ · dom

DOMNamedNodeMap::getNamedItemNS(?string $namespace, string $localName): ?DOMNode

Искать именованный узел по пространству имён и локальному имени, независимо от текстового префикса.

Два аргумента описывают расширенное имя: URI и localName без префикса. В карте атрибутов urn:price и amount выбирают p:amount со значением1200; буква p сама URI не является. Во втором случае неизвестное пространство даёт null и понятное отсутствие цены, а код не подставляет одноимённые данные чужой схемы.

Аргумент / поведениеПолный контракт
$namespaceURI / $localNameURI пространства имён и локальное имя без префикса. null/пустой URI обозначает отсутствие пространства имён. Префикс p не равен URI urn:price.
ВозвратУзел либо null. DTD entities/notations не образуют обычную карту XML-атрибутов с пространствами имён: для них обычно нужен getNamedItem.
Типичная ошибкаЗапрос с URI="p" ищет другое пространство. Атрибут без префикса не получает namespace элемента по умолчанию.
Расширенное имя
  1. p:amount связан с xmlns:p="urn:price"
  2. getNamedItemNS("urn:price", "amount")
  3. Выбор не зависит от того, назван префикс p или price
Проверить поиск по URIСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$node = $items->getNamedItemNS("urn:price", "amount");
echo $node->nodeName, ":", $node->nodeValue, "
";
Ожидаемый результат
p:amount:1200

Для DTD показано отсутствие записи в вымышленном URI; для атрибутов найдено точное расширенное имя.

Обработать неизвестное пространство имёнСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$node = $items->getNamedItemNS("urn:unknown", "amount");
echo $node === null ? "no price in this namespace
" : "found
";
Ожидаемый результат
no price in this namespace

Не подставляем одноимённое значение из чужого пространства. Это предотвращает смешение полей разных контрактов XML.

Связанные операции: DOMNamedNodeMap::getNamedItem.

DOMNamedNodeMap::countПосчитать записи именованной карты перед формированием отчёта или проверкой наличия метаданных.

PHP 5+ · dom

DOMNamedNodeMap::count(): int

Посчитать записи именованной карты перед формированием отчёта или проверкой наличия метаданных.

Для отчёта нужно количество записей: атрибуты товара product. Count возвращает int; length и count(items) читают ту же карту. Сначала сравниваются три формы вызова, затем число сопровождается всеми фактическими именами. Проверка обязательного поля выполняется отдельным поиском, потому что размер карты не доказывает её соответствие формату.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int, эквивалент length/count($map).
СоставСовременная атрибутная карта включает объявления xmlns как Attr; старый DOMNamedNodeMap их не включает. DTD-карта entities/notations содержит соответствующий вид объявлений, не элементы документа.
ГраницаРазмер карты не проверяет схему и обязательные поля. Для обязательного stock проверяйте имя, а не условие count>=2.
Счётчик карты
  1. Именованные записи → карта
  2. count() возвращает количество
  3. Состав всё ещё нужно проверять по именам
Сопоставить доступные счётчикиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
echo $items->count(), ":", $items->length, ":", count($items), "
";
Ожидаемый результат
3:3:3

Число сообщает только размер текущей карты.

Показать метаданные и отдельно проверить обязательное имяСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$names = [];
foreach ($items as $node) {
    $names[] = $node->nodeName;
}
sort($names);
echo 'Записей: ', $items->count(), '; ', implode(', ', $names), "\n";
if ($items->getNamedItem('required') === null) {
    echo "Обязательного имени required нет: импорт не подтверждён\n";
}
Ожидаемый результат
Записей: 3; id, p:amount, stock
Обязательного имени required нет: импорт не подтверждён

Count используется в заголовке реального списка имён. Даже непустая карта не гарантирует конкретную required-запись; её наличие проверяется отдельно по имени. Required здесь условное обязательное поле учебного формата, не особое слово DOM. Сортировка меняет только PHP-массив отчёта, исходная карта не переставляется.

Связанные операции: DOMNamedNodeMap::item, DOMNamedNodeMap::getNamedItem.

DOMNamedNodeMap::getIteratorПостроить список метаданных XML для отчёта без заранее известного количества атрибутов или DTD-объявлений.

PHP 5+ · dom

DOMNamedNodeMap::getIterator(): Iterator

Построить список метаданных XML для отчёта без заранее известного количества атрибутов или DTD-объявлений.

Нужно вывести атрибуты товара product без заранее известного количества. GetIterator возвращает Iterator; foreach получает каждый узел, nodeName читает имя, sort делает отчёт стабильным. Во втором примере в metadata копируются строковые поля, поэтому дальнейшему обработчику не требуется хранить DOM-объекты.

Аргумент / поведениеПолный контракт
ВозвратIterator по узлам карты; foreach($map as $node) использует его автоматически. Ключи итерации не заменяют nodeName/localName/namespaceURI.
Снимокiterator_to_array(..., false) фиксирует набор ссылок перед изменением атрибутов. Для долгого хранения независимо от DOM скопируйте строки в обычный PHP-массив.
СтоимостьПолный обход пропорционален числу записей. DTD из недоверенного источника не нужно разрешать загружать только ради просмотра имени атрибута.
Карта → отчёт
  1. foreach получает объект каждой записи
  2. Берём nodeName, а не сериализуем весь документ
  3. Сортируем имена для стабильного отчёта
Обойти карту и вывести именаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$names = [];
foreach ($items->getIterator() as $node) {
    $names[] = $node->nodeName;
}
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
id,p:amount,stock

Порядок отчёта задан сортировкой явно.

Сохранить строки в независимый массивСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$metadata = [];
foreach ($items as $node) {
    $metadata[$node->nodeName] = $node->nodeValue;
}
echo $metadata["id"], "
";
Ожидаемый результат
p1

В metadata лежат строки имён сущностей либо значений атрибутов, не ссылки на Attr/Entity. Это уже обычный массив для последующей обработки.

Связанные операции: DOMNamedNodeMap::item, DOMNamedNodeMap::count.

Dom\NamedNodeMap::itemПосмотреть один элемент именованной карты по позиции: атрибут либо объявление из DTD.

PHP 8.4+ · dom

Dom\NamedNodeMap::item(int $index): ?Dom\Attr

Посмотреть один элемент именованной карты по позиции: атрибут либо объявление из DTD.

Карта содержит атрибуты товара product. Item принимает индекс и возвращает объект записи либо null. Первый пример намеренно оставляет одну запись, чтобы показать цепочку карта→узел→полезное поле. Затем несколько имён собираются по индексам и сортируются для стабильного отчёта; их позиция не становится бизнес-ключом.

Аргумент / поведениеПолный контракт
$indexЦелочисленная позиция с 0; выход за границы даёт null.
ВозвратУзел или null. В карте атрибутов это Attr; DtdNamedNodeMap выдаёт Entity либо Notation в зависимости от выбранного свойства DTD.
ПорядокНе используйте позицию как смысловой ключ: порядок атрибутов/DTD-объявлений не является правилом бизнеса. Для известного имени используйте getNamedItem/GetNamedItemNS.
Позиция против имени
  1. map содержит именованные записи
  2. item(0) выбирает позицию
  3. getNamedItem("id") выбирает смысловой ключ
Получить атрибут из карты и прочитать его имя и значениеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1"/>');
$items = $document->documentElement->attributes;
foreach ([0, 999] as $index) {
    $attribute = $items->item($index);
    if ($attribute === null) {
        echo 'Атрибута на позиции ', $index, ' нет', "\n";
        continue;
    }
    echo $attribute->nodeName, '=', $attribute->nodeValue, "\n";
}
Ожидаемый результат
id=p1
Атрибута на позиции 999 нет

Attributes возвращает карту, item(0) —объект Attr, nodeName/nodeValue —его имя id и строку p1. В минимальном XML всего один атрибут, поэтому порядок не влияет на пример. Для неизвестного результата сначала обрабатывается null. Имя id в рабочем коде надёжнее искать через getNamedItem.

Собрать стабильный список имён независимо от порядкаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$names = [];
for ($index = 0; $index < $items->length; $index++) {
    $names[] = $items->item($index)->nodeName;
}
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
id,p:amount,stock,xmlns:p

Сортировка делает отчёт детерминированным; сам DOM-каталог не переставляется.

Связанные операции: Dom\NamedNodeMap::getNamedItem, Dom\NamedNodeMap::count.

Dom\NamedNodeMap::getNamedItemПрочитать именованный узел карты: например атрибут id товара или сущность DTD.

PHP 8.4+ · dom

Dom\NamedNodeMap::getNamedItem(string $qualifiedName): ?Dom\Attr

Прочитать именованный узел карты: например атрибут id товара или сущность DTD.

Нужно читать атрибут id товара независимо от порядка карты. Аргумент —строка имени, return —узел, а не его значение. Простой пример читает nodeValue=p1; следующий различает p1, пустой note и отсутствующий missing. Для имени с префиксом, которое может меняться между поставщиками, используется пара URI/localName в getNamedItemNS.

Аргумент / поведениеПолный контракт
$qualifiedNameСтроковое квалифицированное имя, включая префикс при наличии. Для независимости от выбранного префикса используйте getNamedItemNS.
ВозвратСоответствующий узел либо null при отсутствии. Не путайте null с существующим атрибутом, значение которого пустая строка.
МутацияПоиск не создаёт атрибут. Изменение найденного Attr.value изменяет тот же элемент; создание/удаление делается методами Element.
Из имени к объекту
  1. map.getNamedItem("id")
  2. Объект с nodeName=id
  3. У Attr.value содержится значение; у современной Entity nodeValue равен null. null вместо объекта означает отсутствие узла
Получить имя сущности либо значение атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$node = $items->getNamedItem("id");
echo $node->nodeValue, "
";
Ожидаемый результат
p1

Здесь имя известно заранее. Для неизвестного имени сначала проверяйте null.

Отличить пустое значение атрибута от отсутствующего поляСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" note=""/>');
$items = $document->documentElement->attributes;
foreach (['id', 'note', 'missing'] as $name) {
    $node = $items->getNamedItem($name);
    if ($node === null) {
        echo $name, ': поле отсутствует', "\n";
    } else {
        echo $name, ': [', $node->nodeValue, "]\n";
    }
}
Ожидаемый результат
id: [p1]
note: []
missing: поле отсутствует

Return у id и note —объект Attr, даже если note содержит пустую строку. Квадратные скобки делают её видимой. Только отсутствующий missing возвращает null. Это важно для формы импорта: пустое замечание и не переданное поле могут иметь разные правила обновления.

Связанные операции: Dom\NamedNodeMap::getNamedItemNS, Dom\NamedNodeMap::item.

Dom\NamedNodeMap::getNamedItemNSИскать именованный узел по пространству имён и локальному имени, независимо от текстового префикса.

PHP 8.4+ · dom

Dom\NamedNodeMap::getNamedItemNS(?string $namespace, string $localName): ?Dom\Attr

Искать именованный узел по пространству имён и локальному имени, независимо от текстового префикса.

Два аргумента описывают расширенное имя: URI и localName без префикса. В карте атрибутов urn:price и amount выбирают p:amount со значением1200; буква p сама URI не является. Во втором случае неизвестное пространство даёт null и понятное отсутствие цены, а код не подставляет одноимённые данные чужой схемы.

Аргумент / поведениеПолный контракт
$namespaceURI / $localNameURI пространства имён и локальное имя без префикса. null/пустой URI обозначает отсутствие пространства имён. Префикс p не равен URI urn:price.
ВозвратУзел либо null. DTD entities/notations не образуют обычную карту XML-атрибутов с пространствами имён: для них обычно нужен getNamedItem.
Типичная ошибкаЗапрос с URI="p" ищет другое пространство. Атрибут без префикса не получает namespace элемента по умолчанию.
Расширенное имя
  1. p:amount связан с xmlns:p="urn:price"
  2. getNamedItemNS("urn:price", "amount")
  3. Выбор не зависит от того, назван префикс p или price
Проверить поиск по URIСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$node = $items->getNamedItemNS("urn:price", "amount");
echo $node->nodeName, ":", $node->nodeValue, "
";
Ожидаемый результат
p:amount:1200

Для DTD показано отсутствие записи в вымышленном URI; для атрибутов найдено точное расширенное имя.

Обработать неизвестное пространство имёнСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$node = $items->getNamedItemNS("urn:unknown", "amount");
echo $node === null ? "no price in this namespace
" : "found
";
Ожидаемый результат
no price in this namespace

Не подставляем одноимённое значение из чужого пространства. Это предотвращает смешение полей разных контрактов XML.

Связанные операции: Dom\NamedNodeMap::getNamedItem.

Dom\NamedNodeMap::countПосчитать записи именованной карты перед формированием отчёта или проверкой наличия метаданных.

PHP 8.4+ · dom

Dom\NamedNodeMap::count(): int

Посчитать записи именованной карты перед формированием отчёта или проверкой наличия метаданных.

Для отчёта нужно количество записей: атрибуты товара product. Count возвращает int; length и count(items) читают ту же карту. Сначала сравниваются три формы вызова, затем число сопровождается всеми фактическими именами. Проверка обязательного поля выполняется отдельным поиском, потому что размер карты не доказывает её соответствие формату.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int, эквивалент length/count($map).
СоставСовременная атрибутная карта включает объявления xmlns как Attr; старый DOMNamedNodeMap их не включает. DTD-карта entities/notations содержит соответствующий вид объявлений, не элементы документа.
ГраницаРазмер карты не проверяет схему и обязательные поля. Для обязательного stock проверяйте имя, а не условие count>=2.
Счётчик карты
  1. Именованные записи → карта
  2. count() возвращает количество
  3. Состав всё ещё нужно проверять по именам
Сопоставить доступные счётчикиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
echo $items->count(), ":", $items->length, ":", count($items), "
";
Ожидаемый результат
4:4:4

Число сообщает только размер текущей карты.

Показать метаданные и отдельно проверить обязательное имяСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$names = [];
foreach ($items as $node) {
    $names[] = $node->nodeName;
}
sort($names);
echo 'Записей: ', $items->count(), '; ', implode(', ', $names), "\n";
if ($items->getNamedItem('required') === null) {
    echo "Обязательного имени required нет: импорт не подтверждён\n";
}
Ожидаемый результат
Записей: 4; id, p:amount, stock, xmlns:p
Обязательного имени required нет: импорт не подтверждён

Count используется в заголовке реального списка имён. Даже непустая карта не гарантирует конкретную required-запись; её наличие проверяется отдельно по имени. Required здесь условное обязательное поле учебного формата, не особое слово DOM. Сортировка меняет только PHP-массив отчёта, исходная карта не переставляется.

Связанные операции: Dom\NamedNodeMap::item, Dom\NamedNodeMap::getNamedItem.

Dom\NamedNodeMap::getIteratorПостроить список метаданных XML для отчёта без заранее известного количества атрибутов или DTD-объявлений.

PHP 8.4+ · dom

Dom\NamedNodeMap::getIterator(): Iterator

Построить список метаданных XML для отчёта без заранее известного количества атрибутов или DTD-объявлений.

Нужно вывести атрибуты товара product без заранее известного количества. GetIterator возвращает Iterator; foreach получает каждый узел, nodeName читает имя, sort делает отчёт стабильным. Во втором примере в metadata копируются строковые поля, поэтому дальнейшему обработчику не требуется хранить DOM-объекты.

Аргумент / поведениеПолный контракт
ВозвратIterator по узлам карты; foreach($map as $node) использует его автоматически. Ключи итерации не заменяют nodeName/localName/namespaceURI.
Снимокiterator_to_array(..., false) фиксирует набор ссылок перед изменением атрибутов. Для долгого хранения независимо от DOM скопируйте строки в обычный PHP-массив.
СтоимостьПолный обход пропорционален числу записей. DTD из недоверенного источника не нужно разрешать загружать только ради просмотра имени атрибута.
Карта → отчёт
  1. foreach получает объект каждой записи
  2. Берём nodeName, а не сериализуем весь документ
  3. Сортируем имена для стабильного отчёта
Обойти карту и вывести именаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$names = [];
foreach ($items->getIterator() as $node) {
    $names[] = $node->nodeName;
}
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
id,p:amount,stock,xmlns:p

Порядок отчёта задан сортировкой явно.

Сохранить строки в независимый массивСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" xmlns:p="urn:price" p:amount="1200"/>');
$items = $document->documentElement->attributes;
$metadata = [];
foreach ($items as $node) {
    $metadata[$node->nodeName] = $node->nodeValue;
}
echo $metadata["id"], "
";
Ожидаемый результат
p1

В metadata лежат строки имён сущностей либо значений атрибутов, не ссылки на Attr/Entity. Это уже обычный массив для последующей обработки.

Связанные операции: Dom\NamedNodeMap::item, Dom\NamedNodeMap::count.

Dom\DtdNamedNodeMap::itemПосмотреть один элемент именованной карты по позиции: атрибут либо объявление из DTD.

PHP 8.4+ · dom

Dom\DtdNamedNodeMap::item(int $index): Dom\Entity|Dom\Notation|null

Посмотреть один элемент именованной карты по позиции: атрибут либо объявление из DTD.

Карта содержит объявления сущностей из внутренней DTD. Item принимает индекс и возвращает объект записи либо null. Первый пример намеренно оставляет одну запись, чтобы показать цепочку карта→узел→полезное поле. Затем несколько имён собираются по индексам и сортируются для стабильного отчёта; их позиция не становится бизнес-ключом.

Аргумент / поведениеПолный контракт
$indexЦелочисленная позиция с 0; выход за границы даёт null.
ВозвратУзел или null. В карте атрибутов это Attr; DtdNamedNodeMap выдаёт Entity либо Notation в зависимости от выбранного свойства DTD.
ПорядокНе используйте позицию как смысловой ключ: порядок атрибутов/DTD-объявлений не является правилом бизнеса. Для известного имени используйте getNamedItem/GetNamedItemNS.
Позиция против имени
  1. map содержит именованные записи
  2. item(0) выбирает позицию
  3. getNamedItem("id") выбирает смысловой ключ
Прочитать имя единственного объявления и проверить отсутствующую позициюСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea">]><catalog/>');
$items = $document->doctype->entities;
foreach ([0, 999] as $index) {
    $node = $items->item($index);
    if ($node === null) {
        echo 'Объявления на позиции ', $index, ' нет', "\n";
        continue;
    }
    echo 'DTD объявляет сущность: ', $node->nodeName, "\n";
}
Ожидаемый результат
DTD объявляет сущность: tea
Объявления на позиции 999 нет

Карта entities содержит одно объявление, поэтому индекс0 здесь однозначен. Item возвращает Entity, его nodeName даёт tea. Это имя объявления, не текст подстановки Tea и не элемент catalog. При нескольких объявлениях не опирайтесь на их внутренний порядок; следующий отчёт сортирует имена явно.

Собрать стабильный список имён независимо от порядкаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$names = [];
for ($index = 0; $index < $items->length; $index++) {
    $names[] = $items->item($index)->nodeName;
}
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
coffee,tea

Сортировка делает отчёт детерминированным; сам DOM-каталог не переставляется.

Связанные операции: Dom\DtdNamedNodeMap::getNamedItem, Dom\DtdNamedNodeMap::count.

Dom\DtdNamedNodeMap::getNamedItemПрочитать именованный узел карты: например атрибут id товара или сущность DTD.

PHP 8.4+ · dom

Dom\DtdNamedNodeMap::getNamedItem(string $qualifiedName): Dom\Entity|Dom\Notation|null

Прочитать именованный узел карты: например атрибут id товара или сущность DTD.

Нужно читать конкретное объявление tea независимо от порядка карты. Аргумент —строка имени, return —узел, а не его значение. Простой пример читает nodeName=tea; следующий обрабатывает отсутствующее объявление. Для имени с префиксом, которое может меняться между поставщиками, используется пара URI/localName в getNamedItemNS.

Аргумент / поведениеПолный контракт
$qualifiedNameСтроковое квалифицированное имя, включая префикс при наличии. Для независимости от выбранного префикса используйте getNamedItemNS.
ВозвратСоответствующий узел либо null при отсутствии. Не путайте null с существующим атрибутом, значение которого пустая строка.
МутацияПоиск не создаёт атрибут. Изменение найденного Attr.value изменяет тот же элемент; создание/удаление делается методами Element.
Из имени к объекту
  1. map.getNamedItem("tea")
  2. Объект с nodeName=tea
  3. У Attr.value содержится значение; у современной Entity nodeValue равен null. null вместо объекта означает отсутствие узла
Получить имя сущности либо значение атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$node = $items->getNamedItem("tea");
echo $node->nodeName, "
";
Ожидаемый результат
tea

Здесь имя известно заранее. Для неизвестного имени сначала проверяйте null.

Проверить нужные объявления формата по именамСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
foreach (['tea', 'missing'] as $name) {
    $node = $items->getNamedItem($name);
    if ($node === null) {
        echo 'В DTD нет ', $name, ': проверьте версию формата', "\n";
    } else {
        echo 'Объявлено имя: ', $node->nodeName, "\n";
    }
}
Ожидаемый результат
Объявлено имя: tea
В DTD нет missing: проверьте версию формата

GetNamedItem получает имя объявления и возвращает Entity. Null обрабатывается до чтения nodeName. Проверка наличия имени помогает диагностировать ожидаемый формат, но не валидирует весь документ и не является разрешением загружать внешние DTD. Здесь объявления записаны прямо в учебной строке.

Связанные операции: Dom\DtdNamedNodeMap::getNamedItemNS, Dom\DtdNamedNodeMap::item.

Dom\DtdNamedNodeMap::getNamedItemNSИскать именованный узел по пространству имён и локальному имени, независимо от текстового префикса.

PHP 8.4+ · dom

Dom\DtdNamedNodeMap::getNamedItemNS(?string $namespace, string $localName): Dom\Entity|Dom\Notation|null

Искать именованный узел по пространству имён и локальному имени, независимо от текстового префикса.

Два аргумента описывают расширенное имя: URI и localName без префикса. В DTD-карте такой поиск не находит выдуманную цену; затем показано настоящее объявление tea через getNamedItem. Во втором случае неизвестное пространство даёт null и понятное отсутствие цены, а код не подставляет одноимённые данные чужой схемы.

Аргумент / поведениеПолный контракт
$namespaceURI / $localNameURI пространства имён и локальное имя без префикса. null/пустой URI обозначает отсутствие пространства имён. Префикс p не равен URI urn:price.
ВозвратУзел либо null. DTD entities/notations не образуют обычную карту XML-атрибутов с пространствами имён: для них обычно нужен getNamedItem.
Типичная ошибкаЗапрос с URI="p" ищет другое пространство. Атрибут без префикса не получает namespace элемента по умолчанию.
Расширенное имя
  1. p:amount связан с xmlns:p="urn:price"
  2. getNamedItemNS("urn:price", "amount")
  3. Выбор не зависит от того, назван префикс p или price
Не искать DTD-объявление как атрибут пространства ценыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$price = $items->getNamedItemNS('urn:price', 'amount');
if ($price === null) {
    echo "DTD-карта не содержит атрибут цены urn:price/amount\n";
}
$entity = $items->getNamedItem('tea');
echo 'Объявление по имени: ', $entity->nodeName, "\n";
Ожидаемый результат
DTD-карта не содержит атрибут цены urn:price/amount
Объявление по имени: tea

GetNamedItemNS вернул null: карта entities хранит объявления сущностей, а не атрибуты product. После этого правильный для задачи getNamedItem(tea) даёт Entity. Наличие общего метода в интерфейсе карты не превращает DTD в словарь XML-атрибутов.

Обработать неизвестное пространство имёнСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$node = $items->getNamedItemNS("urn:unknown", "amount");
echo $node === null ? "no price in this namespace
" : "found
";
Ожидаемый результат
no price in this namespace

Не подставляем одноимённое значение из чужого пространства. Это предотвращает смешение полей разных контрактов XML.

Связанные операции: Dom\DtdNamedNodeMap::getNamedItem.

Dom\DtdNamedNodeMap::countПосчитать записи именованной карты перед формированием отчёта или проверкой наличия метаданных.

PHP 8.4+ · dom

Dom\DtdNamedNodeMap::count(): int

Посчитать записи именованной карты перед формированием отчёта или проверкой наличия метаданных.

Для отчёта нужно количество записей: объявления сущностей из внутренней DTD. Count возвращает int; length и count(items) читают ту же карту. Сначала сравниваются три формы вызова, затем число сопровождается всеми фактическими именами. Проверка обязательного поля выполняется отдельным поиском, потому что размер карты не доказывает её соответствие формату.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; int, эквивалент length/count($map).
СоставСовременная атрибутная карта включает объявления xmlns как Attr; старый DOMNamedNodeMap их не включает. DTD-карта entities/notations содержит соответствующий вид объявлений, не элементы документа.
ГраницаРазмер карты не проверяет схему и обязательные поля. Для обязательного stock проверяйте имя, а не условие count>=2.
Счётчик карты
  1. Именованные записи → карта
  2. count() возвращает количество
  3. Состав всё ещё нужно проверять по именам
Сопоставить доступные счётчикиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
echo $items->count(), ":", $items->length, ":", count($items), "
";
Ожидаемый результат
2:2:2

Число сообщает только размер текущей карты.

Показать метаданные и отдельно проверить обязательное имяСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$names = [];
foreach ($items as $node) {
    $names[] = $node->nodeName;
}
sort($names);
echo 'Записей: ', $items->count(), '; ', implode(', ', $names), "\n";
if ($items->getNamedItem('required') === null) {
    echo "Обязательного имени required нет: импорт не подтверждён\n";
}
Ожидаемый результат
Записей: 2; coffee, tea
Обязательного имени required нет: импорт не подтверждён

Count используется в заголовке реального списка имён. Даже непустая карта не гарантирует конкретную required-запись; её наличие проверяется отдельно по имени. Required здесь условное обязательное поле учебного формата, не особое слово DOM. Сортировка меняет только PHP-массив отчёта, исходная карта не переставляется.

Связанные операции: Dom\DtdNamedNodeMap::item, Dom\DtdNamedNodeMap::getNamedItem.

Dom\DtdNamedNodeMap::getIteratorПостроить список метаданных XML для отчёта без заранее известного количества атрибутов или DTD-объявлений.

PHP 8.4+ · dom

Dom\DtdNamedNodeMap::getIterator(): Iterator

Построить список метаданных XML для отчёта без заранее известного количества атрибутов или DTD-объявлений.

Нужно вывести объявления сущностей из внутренней DTD без заранее известного количества. GetIterator возвращает Iterator; foreach получает каждый узел, nodeName читает имя, sort делает отчёт стабильным. Во втором примере в metadata копируются строковые поля, поэтому дальнейшему обработчику не требуется хранить DOM-объекты.

Аргумент / поведениеПолный контракт
ВозвратIterator по узлам карты; foreach($map as $node) использует его автоматически. Ключи итерации не заменяют nodeName/localName/namespaceURI.
Снимокiterator_to_array(..., false) фиксирует набор ссылок перед изменением атрибутов. Для долгого хранения независимо от DOM скопируйте строки в обычный PHP-массив.
СтоимостьПолный обход пропорционален числу записей. DTD из недоверенного источника не нужно разрешать загружать только ради просмотра имени атрибута.
Карта → отчёт
  1. foreach получает объект каждой записи
  2. Берём nodeName, а не сериализуем весь документ
  3. Сортируем имена для стабильного отчёта
Обойти карту и вывести именаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$names = [];
foreach ($items->getIterator() as $node) {
    $names[] = $node->nodeName;
}
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
coffee,tea

Порядок отчёта задан сортировкой явно.

Сохранить строки в независимый массивСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea"><!ENTITY coffee "Coffee">]><catalog/>');
$items = $document->doctype->entities;
$metadata = [];
foreach ($items as $node) {
    $metadata[$node->nodeName] = $node->nodeName;
}
echo $metadata["tea"], "
";
Ожидаемый результат
tea

В metadata лежат строки имён сущностей либо значений атрибутов, не ссылки на Attr/Entity. Это уже обычный массив для последующей обработки.

Связанные операции: Dom\DtdNamedNodeMap::item, Dom\DtdNamedNodeMap::count.

DOMElement::getAttributeПрочитать значение поля XML и использовать его в расчёте после проверки формата.

PHP 5+ · dom

DOMElement::getAttribute(string $qualifiedName): string

Прочитать значение поля XML и использовать его в расчёте после проверки формата.

Аргумент / поведениеПолный контракт
qualifiedNameИмя атрибута строкой; если есть префикс, он входит в имя.
ВозвратСтрока существующего значения; отсутствующий атрибут → пустая строка. Пустой существующий атрибут имеет значение "".
НазначениеПрочитать поле, затем отдельно проверить/преобразовать строку. Значение stock="4" не превращается автоматически в int.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Первое действие с полем товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
echo $product->getAttribute("stock"), "
";
echo $product->hasAttribute("missing") ? "exists
" : "absent
";
Ожидаемый результат
4
absent

Значения остаются строками XML; бизнес-проверки выполняются отдельно.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$raw = $product->getAttribute("stock");
if ($raw !== null && ctype_digit($raw)) {
    echo (int) $raw + 2, "
";
}
Ожидаемый результат
6

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::getAttributeNS.

DOMElement::getAttributeNSПолучить поле интеграции по устойчивому URI, не привязываясь к выбранному поставщиком префиксу.

PHP 5+ · dom

DOMElement::getAttributeNS(?string $namespace, string $localName): string

Получить поле интеграции по устойчивому URI, не привязываясь к выбранному поставщиком префиксу.

Аргумент / поведениеПолный контракт
namespace / localNameURI (null для отсутствующего пространства) и локальное имя без префикса. Не передавайте p:amount вместо amount.
ВозвратСтрока значения; отсутствие → пустая строка.
Пространства имёнxmlns:p="urn:price" связывает префикс и URI. Другой документ может назвать тот же URI префиксом price; поиск по URI продолжит работать.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Первое действие с полем товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
1200

Значения остаются строками XML; бизнес-проверки выполняются отдельно.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
echo $product->getAttributeNS(null, "stock"), "
";
var_dump($product->hasAttributeNS("urn:price", "stock"));
Ожидаемый результат
4
bool(false)

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::getAttribute.

DOMElement::hasAttributeРазличить поле без значения и поле, которое вообще не передали.

PHP 5+ · dom

DOMElement::hasAttribute(string $qualifiedName): bool

Различить поле без значения и поле, которое вообще не передали.

Аргумент / поведениеПолный контракт
qualifiedNameТочное имя атрибута.
Возвратbool: существует ли атрибут, включая существующее пустое значение. Это не проверка истинности строки.
ОсобенностьВ HTML disabled="false" всё равно означает присутствующий булев атрибут. Для снятия требуется removeAttribute, а не строка false.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Отличить пустой комментарий от отсутствующей скидкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" comment=""/>');
$product = $document->documentElement;
echo $product->hasAttribute('comment') ? "Комментарий передан, но пуст\n" : "Комментарий не передан\n";
echo $product->hasAttribute('discount') ? "Скидка передана\n" : "Скидки нет: используем базовую цену\n";
Ожидаемый результат
Комментарий передан, но пуст
Скидки нет: используем базовую цену

hasAttribute проверяет наличие имени. Пустая строка значения не означает отсутствующего поля; эти два состояния могут по-разному менять запись при обновлении.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
if (!$product->hasAttribute("currency")) {
    $product->setAttribute("currency", "RUB");
}
echo $product->getAttribute("currency"), "
";
Ожидаемый результат
RUB

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::getAttribute.

DOMElement::hasAttributeNSПроверить наличие поля конкретной версии/схемы интеграции.

PHP 5+ · dom

DOMElement::hasAttributeNS(?string $namespace, string $localName): bool

Проверить наличие поля конкретной версии/схемы интеграции.

Аргумент / поведениеПолный контракт
namespace / localNameURI пространства и локальное имя. Атрибут без префикса не наследует namespace элемента по умолчанию.
Возвратbool наличия точного расширенного имени; не проверяет его значение и допустимость по схеме.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Принять цену только из договорённого словаряСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price" sku="A1" p:amount="1200"/>');
$product = $document->documentElement;
if ($product->hasAttributeNS('urn:price', 'amount')) {
    echo 'Цена: ', $product->getAttributeNS('urn:price', 'amount'), "\n";
}
if (!$product->hasAttributeNS('urn:delivery', 'amount')) { echo "Стоимость доставки не передана\n"; }
Ожидаемый результат
Цена: 1200
Стоимость доставки не передана

У одинакового localName amount могут быть разные URI. Проверяем расширенное имя, затем читаем значение. Это ещё не проверка валюты/диапазона цены.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
if ($product->hasAttributeNS("urn:price", "amount")) {
    echo "price field supplied
";
}
Ожидаемый результат
price field supplied

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::getAttributeNS.

DOMElement::getAttributeNamesПолучить список полей для диагностики входящего XML, когда состав заранее неизвестен.

PHP 8.0+ · dom

DOMElement::getAttributeNames(): array

Получить список полей для диагностики входящего XML, когда состав заранее неизвестен.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; array<string> квалифицированных имён атрибутов.
Объявления xmlnsВ обоих семействах getAttributeNames включает объявления xmlns. В legacy это отличается от attributes/DOMNamedNodeMap, где namespace-объявления отделены от обычных Attr.
ПорядокДля сравнения отчётов сортируйте копию массива. Не выводите из позиции атрибута его смысл/важность.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Первое действие с полем товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$names = $product->getAttributeNames();
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
empty,id,p:amount,stock,xmlns:p

Значения остаются строками XML; бизнес-проверки выполняются отдельно.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
foreach ($product->getAttributeNames() as $name) {
    if (str_starts_with($name, "p:")) {
        echo $name, "
";
    }
}
Ожидаемый результат
p:amount

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::getAttribute.

DOMElement::setAttributeИзменить остаток либо добавить поле экспорта, поручив DOM корректное XML-экранирование.

PHP 5+ · dom

DOMElement::setAttribute(string $qualifiedName, string $value): DOMAttr|bool

Изменить остаток либо добавить поле экспорта, поручив DOM корректное XML-экранирование.

Аргумент / поведениеПолный контракт
qualifiedName / valueИмя и строковое значение. Значение передаётся как текст: вручную htmlspecialchars перед DOM не нужен, иначе появится двойное кодирование.
Возвратvoid в modern; DOMAttr|bool в legacy. В обоих случаях меняется элемент, а не возвращается его копия.
ОшибкиНедопустимое XML-имя → DOMException INVALID_CHARACTER_ERR; защищённая структура → NO_MODIFICATION_ALLOWED_ERR. Имя и данные проверяются раздельно.
ВыборДля namespace используйте setAttributeNS. Имена полей от клиента ограничивайте схемой, значения URL/HTML-поведения проверяйте по назначению.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Обновить остаток в XML товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->setAttribute("stock", "6");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="6"/>

setAttribute меняет выбранное поле. saveXml превращает изменённый узел в строку; другие атрибуты остаются.

Передать амперсанд как данные и увидеть XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->setAttribute("label", "Tea & coffee");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="4" label="Tea &amp; coffee"/>

Передаём обычный &, сериализатор записывает &. Ручной htmlspecialchars перед setAttribute дал бы двойное экранирование.

Связанные операции: DOMElement::setAttributeNS.

DOMElement::setAttributeNSДобавить сведения другой подсистемы, не смешивая одноимённые поля XML-контрактов.

PHP 5+ · dom

DOMElement::setAttributeNS(?string $namespace, string $qualifiedName, string $value): void

Добавить сведения другой подсистемы, не смешивая одноимённые поля XML-контрактов.

Аргумент / поведениеПолный контракт
namespace / qualifiedName / valueURI (или null), имя с согласованным префиксом и строка значения. localName amount отличается от qualifiedName p:amount.
Возвратvoid в modern; void/false при ошибке в legacy с выключенной строгой обработкой.
ОшибкиINVALID_CHARACTER_ERR для имени; NAMESPACE_ERR для префикса без URI, неверных комбинаций xml/xmlns и зарезервированных URI. NUL и неверные типы тоже не являются допустимыми данными.
СериализацияDOM создаёт/сохраняет необходимые объявления префикса при сериализации. Не собирайте xmlns вручную строковыми заменами.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Изменить цену в словаре urn:priceСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price" sku="A1" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttributeNS("urn:price", "p:amount", "1500");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product xmlns:p="urn:price" sku="A1" p:amount="1500"/>

URI и локальное имя определяют поле; p остаётся краткой записью URI. На выходе есть объявление xmlns:p.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttributeNS("urn:delivery", "d:days", "2");
echo $product->getAttributeNS("urn:delivery", "days"), "
";
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
2
1200

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::setAttribute.

DOMElement::removeAttributeУбрать необязательное или устаревшее поле из XML, сохранив остальные атрибуты.

PHP 5+ · dom

DOMElement::removeAttribute(string $qualifiedName): bool

Убрать необязательное или устаревшее поле из XML, сохранив остальные атрибуты.

Аргумент / поведениеПолный контракт
qualifiedNameИмя удаляемого атрибута; отсутствие не требует предварительного создания/замены.
Возвратvoid в modern; bool в legacy. Удаляет поле, а не присваивает пустую строку.
ОшибкаNO_MODIFICATION_ALLOWED_ERR для защищённого изменения; strictErrorChecking старого DOM влияет на форму сообщения/false.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Убрать остаток из публичного XML товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->removeAttribute("stock");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1"/>

Поля stock больше нет; это отличается от stock="". Так можно готовить отдельный публичный набор данных.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->removeAttribute("empty");
$product->removeAttribute("missing");
echo $product->getAttribute("id"), "
";
Ожидаемый результат
p1

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::removeAttributeNS.

DOMElement::removeAttributeNSУдалить поле одной интеграции, не трогая похожие имена в другом пространстве.

PHP 5+ · dom

DOMElement::removeAttributeNS(?string $namespace, string $localName): void

Удалить поле одной интеграции, не трогая похожие имена в другом пространстве.

Аргумент / поведениеПолный контракт
namespace / localNameURI и локальное имя. Используйте тот же URI, по которому поле читалось, а не его префикс.
Возвратvoid; отсутствие подходящего атрибута не создаёт ошибочное пустое поле.
ОтличиеУдаление объявления xmlns в legacy имеет особенности namespace-узлов. Здесь речь об обычном атрибуте в заданном URI.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Удалить цену выбранной интеграцииСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price" sku="A1" p:amount="1200"/>');
$product = $document->documentElement;
$product->removeAttributeNS("urn:price", "amount");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product xmlns:p="urn:price" sku="A1"/>

Удалено само поле p:amount. Объявление xmlns:p осталось: отсутствие атрибута и очистка деклараций namespace — разные операции.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->removeAttributeNS("urn:unknown", "amount");
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
1200

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: DOMElement::removeAttribute.

DOMElement::toggleAttributeСогласовать булев атрибут со статусом товара без ручной пары set/remove.

PHP 8.3+ · dom

DOMElement::toggleAttribute(string $qualifiedName, ?bool $force = null): bool

Согласовать булев атрибут со статусом товара без ручной пары set/remove.

Аргумент / поведениеПолный контракт
qualifiedName / force=nullnull переключает наличие; true обеспечивает наличие пустого атрибута, false удаляет. Если атрибут уже есть и force=true, значение сохраняется.
Возвратbool наличия ПОСЛЕ операции; false может означать успешное удаление, не ошибку.
ОшибкиНедопустимое имя → DOMException. В HTML имя нормализуется по правилам HTML; XML остаётся чувствительным к регистру.
ПовторяемостьДля повторной генерации используйте force с реальным состоянием, а не слепое переключение.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Два переключения отметки selectedСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->toggleAttribute("selected");
echo $document->saveXml($product), "\n";
$product->toggleAttribute("selected");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="4" selected=""/>
<product sku="A1" stock="4"/>

Первый вызов добавил пустой атрибут, второй удалил его. Возврат bool сообщает наличие после операции, но результат изменения видно в XML.

Повторить синхронизацию статуса без случайного снятияСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$inStock = false;
$product->toggleAttribute('unavailable', !$inStock);
$product->toggleAttribute('unavailable', !$inStock);
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="4" unavailable=""/>

Второй аргумент force задаёт желаемое наличие. Повторная генерация с тем же состоянием сохраняет отметку. $inStock здесь отдельное демонстрационное состояние, а не вычисление из stock.

Связанные операции: DOMElement::hasAttribute.

DOMElement::getAttributeNodeПолучить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

PHP 5+ · dom

DOMElement::getAttributeNode(string $qualifiedName): DOMAttr|DOMNameSpaceNode|false

Получить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

Аргумент / поведениеПолный контракт
АргументыКвалифицированное имя атрибута
ВозвратDom\Attr|null в modern; DOMAttr|DOMNameSpaceNode|false в legacy (namespace-объявление представляется особым объектом). Отсутствие не равно пустому значению.
СвязьAttr.value связан с исходным элементом. Это не копия значения; для строки достаточно getAttribute.
НаследованиеAttr — узел, но не обычный дочерний Element; его ownerElement важнее parentNode.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Прочитать объект атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
echo $attribute->value, "
";
Ожидаемый результат
4

Стрелка value обращается к свойству Attr.

Изменить значение через полученный объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
$attribute->value = "8";
echo $product->getAttribute("stock"), "
";
Ожидаемый результат
8

И объект атрибута, и Element читают одно изменённое значение.

Связанные операции: DOMElement::getAttribute, DOMElement::removeAttributeNode.

DOMElement::setAttributeNodeПрисоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

PHP 5+ · dom

DOMElement::setAttributeNode(DOMAttr $attr): DOMAttr|null|false

Присоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

Аргумент / поведениеПолный контракт
$attrAttr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму.
ВозвратСтарый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом.
ВладениеModern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR).
ПрактикаДля обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Заменить существующее поле и прочитать староеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttribute("stock");
$attribute->value = "8";
$old = $product->setAttributeNode($attribute);
echo $old->value, ":", $attribute->value, "
";
Ожидаемый результат
4:8

Старый атрибут отсоединён, но его объект ещё доступен переменной old.

Повторно передать тот же объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttribute("stock");
$attribute->value = "8";
$product->setAttributeNode($attribute);
var_dump($product->setAttributeNode($attribute));
Ожидаемый результат
NULL

Повторное присоединение именно того же объекта не заменяет его новой копией.

Связанные операции: DOMElement::setAttribute, DOMElement::getAttributeNode.

DOMElement::getAttributeNodeNSПолучить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

PHP 5+ · dom

DOMElement::getAttributeNodeNS(?string $namespace, string $localName): DOMAttr|DOMNameSpaceNode|null

Получить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

Аргумент / поведениеПолный контракт
АргументыURI + localName
ВозвратDom\Attr|null в modern; DOMAttr|DOMNameSpaceNode|false в legacy (namespace-объявление представляется особым объектом). Отсутствие не равно пустому значению.
СвязьAttr.value связан с исходным элементом. Это не копия значения; для строки достаточно getAttribute.
НаследованиеAttr — узел, но не обычный дочерний Element; его ownerElement важнее parentNode.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Прочитать объект атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNodeNS("urn:price", "amount");
echo $attribute->value, "
";
Ожидаемый результат
1200

Стрелка value обращается к свойству Attr.

Изменить значение через полученный объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNodeNS("urn:price", "amount");
$attribute->value = "8";
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
8

И объект атрибута, и Element читают одно изменённое значение.

Связанные операции: DOMElement::getAttributeNS, DOMElement::removeAttributeNode.

DOMElement::setAttributeNodeNSПрисоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

PHP 5+ · dom

DOMElement::setAttributeNodeNS(DOMAttr $attr): DOMAttr|null|false

Присоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

Аргумент / поведениеПолный контракт
$attrAttr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму.
ВозвратСтарый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом.
ВладениеModern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR).
ПрактикаДля обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Заменить существующее поле и прочитать староеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttributeNS("urn:price", "p:amount");
$attribute->value = "8";
$old = $product->setAttributeNodeNS($attribute);
echo $old->value, ":", $attribute->value, "
";
Ожидаемый результат
1200:8

Старый атрибут отсоединён, но его объект ещё доступен переменной old.

Повторно передать тот же объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttributeNS("urn:price", "p:amount");
$attribute->value = "8";
$product->setAttributeNodeNS($attribute);
var_dump($product->setAttributeNodeNS($attribute));
Ожидаемый результат
NULL

Повторное присоединение именно того же объекта не заменяет его новой копией.

Связанные операции: DOMElement::setAttributeNS, DOMElement::getAttributeNodeNS.

DOMElement::removeAttributeNodeУдалить конкретный объект Attr, например после обработки поля, и оставить объект для журнала изменений.

PHP 5+ · dom

DOMElement::removeAttributeNode(DOMAttr $attr): DOMAttr|false

Удалить конкретный объект Attr, например после обработки поля, и оставить объект для журнала изменений.

Аргумент / поведениеПолный контракт
$attrАтрибут, принадлежащий этому элементу; одно совпадающее имя у другого объекта недостаточно.
ВозвратУдалённый Attr; legacy может false при нестрогой ошибке. В старом тексте manual встречается true — фактический API возвращает объект.
ОшибкиNOT_FOUND_ERR для не принадлежащего элементу объекта; NO_MODIFICATION_ALLOWED_ERR для защищённого изменения.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Сохранить снятое значение и показать оставшийся товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode('stock');
$removed = $product->removeAttributeNode($attribute);
echo 'Снято поле stock=', $removed->value, "\n";
echo $document->saveXml($product), "\n";
Ожидаемый результат
Снято поле stock=4
<product sku="A1"/>

Возвращён снятый Attr, и его значение можно использовать в журнале изменений. Сам товар уже не содержит этого поля.

Снять поле один раз, проверяя его владельцаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
$product->removeAttributeNode($attribute);
if ($attribute->ownerElement === null) {
    echo "detached
";
}
Ожидаемый результат
detached

Для повторного вызова сначала проверяем ownerElement: повторно удалить уже отсоединённый Attr нельзя.

Связанные операции: DOMElement::removeAttribute, DOMElement::getAttributeNode.

DOMElement::setIdAttributeОбъявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

PHP 5+ · dom

DOMElement::setIdAttribute(string $qualifiedName, bool $isId): void

Объявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

Аргумент / поведениеПолный контракт
ПолеКвалифицированное имя существующего атрибута
$isIdtrue помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku.
Возврат / ошибкиvoid; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении.
ГраницаID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка.
Поле становится ключом DOM
  1. product sku="SKU-1"
  2. setIdAttribute*(…, true)
  3. document.getElementById("SKU-1") → этот product
Найти товар по отмеченному атрибутуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttribute("sku", "SKU-1");
$product->setIdAttribute("sku", true);
echo $document->getElementById("SKU-1")->nodeName, "
";
Ожидаемый результат
product

Помечается уже существующее поле со значением SKU-1.

Снять роль ID, сохранив артикул товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="SKU-1">Desk</product>');
$product = $document->documentElement;
$product->setIdAttribute("sku", true);
$product->setIdAttribute("sku", false);
echo $document->saveXml($product), "\n";
echo $document->getElementById("SKU-1") === null ? "Поле сохранено, поиск по ID отключён\n" : "Найдено\n";
Ожидаемый результат
<product sku="SKU-1">Desk</product>
Поле сохранено, поиск по ID отключён

false снимает пользовательскую пометку ID, а не удаляет атрибут или его текст. Видимый XML подтверждает сохранность поля.

Связанные операции: DOMDocument::getElementById, DOMAttr::isId.

DOMElement::setIdAttributeNSОбъявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

PHP 5+ · dom

DOMElement::setIdAttributeNS(string $namespace, string $qualifiedName, bool $isId): void

Объявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

Аргумент / поведениеПолный контракт
ПолеURI + localName существующего атрибута
$isIdtrue помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku.
Возврат / ошибкиvoid; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении.
ГраницаID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка.
Поле становится ключом DOM
  1. product sku="SKU-1"
  2. setIdAttribute*(…, true)
  3. document.getElementById("SKU-1") → этот product
Найти товар по отмеченному атрибутуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttributeNS("urn:key", "k:sku", "SKU-1");
$product->setIdAttributeNS("urn:key", "sku", true);
echo $document->getElementById("SKU-1")->nodeName, "
";
Ожидаемый результат
product

Помечается уже существующее поле со значением SKU-1.

Снять роль ID, сохранив артикул товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:k="urn:key" k:sku="SKU-1">Desk</product>');
$product = $document->documentElement;
$product->setIdAttributeNS("urn:key", "sku", true);
$product->setIdAttributeNS("urn:key", "sku", false);
echo $document->saveXml($product), "\n";
echo $document->getElementById("SKU-1") === null ? "Поле сохранено, поиск по ID отключён\n" : "Найдено\n";
Ожидаемый результат
<product xmlns:k="urn:key" k:sku="SKU-1">Desk</product>
Поле сохранено, поиск по ID отключён

false снимает пользовательскую пометку ID, а не удаляет атрибут или его текст. Видимый XML подтверждает сохранность поля.

Связанные операции: DOMDocument::getElementById, DOMAttr::isId.

DOMElement::setIdAttributeNodeОбъявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

PHP 5+ · dom

DOMElement::setIdAttributeNode(DOMAttr $attr, bool $isId): void

Объявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

Аргумент / поведениеПолный контракт
ПолеAttr-объект этого элемента
$isIdtrue помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku.
Возврат / ошибкиvoid; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении.
ГраницаID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка.
Поле становится ключом DOM
  1. product sku="SKU-1"
  2. setIdAttribute*(…, true)
  3. document.getElementById("SKU-1") → этот product
Найти товар по отмеченному атрибутуСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttribute("sku", "SKU-1");
$product->setIdAttributeNode($product->getAttributeNode("sku"), true);
echo $document->getElementById("SKU-1")->nodeName, "
";
Ожидаемый результат
product

Помечается уже существующее поле со значением SKU-1.

Снять роль ID, сохранив артикул товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="SKU-1">Desk</product>');
$product = $document->documentElement;
$product->setIdAttributeNode($product->getAttributeNode("sku"), true);
$product->setIdAttributeNode($product->getAttributeNode("sku"), false);
echo $document->saveXml($product), "\n";
echo $document->getElementById("SKU-1") === null ? "Поле сохранено, поиск по ID отключён\n" : "Найдено\n";
Ожидаемый результат
<product sku="SKU-1">Desk</product>
Поле сохранено, поиск по ID отключён

false снимает пользовательскую пометку ID, а не удаляет атрибут или его текст. Видимый XML подтверждает сохранность поля.

Связанные операции: DOMDocument::getElementById, DOMAttr::isId.

Dom\Element::getAttributeПрочитать значение поля XML и использовать его в расчёте после проверки формата.

PHP 8.4+ · dom

Dom\Element::getAttribute(string $qualifiedName): ?string

Прочитать значение поля XML и использовать его в расчёте после проверки формата.

Аргумент / поведениеПолный контракт
qualifiedNameИмя атрибута строкой; если есть префикс, он входит в имя.
ВозвратСтрока существующего значения; отсутствующий атрибут → null. Пустой существующий атрибут имеет значение "".
НазначениеПрочитать поле, затем отдельно проверить/преобразовать строку. Значение stock="4" не превращается автоматически в int.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Первое действие с полем товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
echo $product->getAttribute("stock"), "
";
echo $product->hasAttribute("missing") ? "exists
" : "absent
";
Ожидаемый результат
4
absent

Значения остаются строками XML; бизнес-проверки выполняются отдельно.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$raw = $product->getAttribute("stock");
if ($raw !== null && ctype_digit($raw)) {
    echo (int) $raw + 2, "
";
}
Ожидаемый результат
6

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::getAttributeNS.

Dom\Element::getAttributeNSПолучить поле интеграции по устойчивому URI, не привязываясь к выбранному поставщиком префиксу.

PHP 8.4+ · dom

Dom\Element::getAttributeNS(?string $namespace, string $localName): ?string

Получить поле интеграции по устойчивому URI, не привязываясь к выбранному поставщиком префиксу.

Аргумент / поведениеПолный контракт
namespace / localNameURI (null для отсутствующего пространства) и локальное имя без префикса. Не передавайте p:amount вместо amount.
ВозвратСтрока значения; отсутствие → null.
Пространства имёнxmlns:p="urn:price" связывает префикс и URI. Другой документ может назвать тот же URI префиксом price; поиск по URI продолжит работать.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Первое действие с полем товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
1200

Значения остаются строками XML; бизнес-проверки выполняются отдельно.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
echo $product->getAttributeNS(null, "stock"), "
";
var_dump($product->hasAttributeNS("urn:price", "stock"));
Ожидаемый результат
4
bool(false)

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::getAttribute.

Dom\Element::hasAttributeРазличить поле без значения и поле, которое вообще не передали.

PHP 8.4+ · dom

Dom\Element::hasAttribute(string $qualifiedName): bool

Различить поле без значения и поле, которое вообще не передали.

Аргумент / поведениеПолный контракт
qualifiedNameТочное имя атрибута.
Возвратbool: существует ли атрибут, включая существующее пустое значение. Это не проверка истинности строки.
ОсобенностьВ HTML disabled="false" всё равно означает присутствующий булев атрибут. Для снятия требуется removeAttribute, а не строка false.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Отличить пустой комментарий от отсутствующей скидкиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" comment=""/>');
$product = $document->documentElement;
echo $product->hasAttribute('comment') ? "Комментарий передан, но пуст\n" : "Комментарий не передан\n";
echo $product->hasAttribute('discount') ? "Скидка передана\n" : "Скидки нет: используем базовую цену\n";
Ожидаемый результат
Комментарий передан, но пуст
Скидки нет: используем базовую цену

hasAttribute проверяет наличие имени. Пустая строка значения не означает отсутствующего поля; эти два состояния могут по-разному менять запись при обновлении.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
if (!$product->hasAttribute("currency")) {
    $product->setAttribute("currency", "RUB");
}
echo $product->getAttribute("currency"), "
";
Ожидаемый результат
RUB

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::getAttribute.

Dom\Element::hasAttributeNSПроверить наличие поля конкретной версии/схемы интеграции.

PHP 8.4+ · dom

Dom\Element::hasAttributeNS(?string $namespace, string $localName): bool

Проверить наличие поля конкретной версии/схемы интеграции.

Аргумент / поведениеПолный контракт
namespace / localNameURI пространства и локальное имя. Атрибут без префикса не наследует namespace элемента по умолчанию.
Возвратbool наличия точного расширенного имени; не проверяет его значение и допустимость по схеме.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Принять цену только из договорённого словаряСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:p="urn:price" sku="A1" p:amount="1200"/>');
$product = $document->documentElement;
if ($product->hasAttributeNS('urn:price', 'amount')) {
    echo 'Цена: ', $product->getAttributeNS('urn:price', 'amount'), "\n";
}
if (!$product->hasAttributeNS('urn:delivery', 'amount')) { echo "Стоимость доставки не передана\n"; }
Ожидаемый результат
Цена: 1200
Стоимость доставки не передана

У одинакового localName amount могут быть разные URI. Проверяем расширенное имя, затем читаем значение. Это ещё не проверка валюты/диапазона цены.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
if ($product->hasAttributeNS("urn:price", "amount")) {
    echo "price field supplied
";
}
Ожидаемый результат
price field supplied

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::getAttributeNS.

Dom\Element::getAttributeNamesПолучить список полей для диагностики входящего XML, когда состав заранее неизвестен.

PHP 8.4+ · dom

Dom\Element::getAttributeNames(): array

Получить список полей для диагностики входящего XML, когда состав заранее неизвестен.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; array<string> квалифицированных имён атрибутов.
Объявления xmlnsВ обоих семействах getAttributeNames включает объявления xmlns. В legacy это отличается от attributes/DOMNamedNodeMap, где namespace-объявления отделены от обычных Attr.
ПорядокДля сравнения отчётов сортируйте копию массива. Не выводите из позиции атрибута его смысл/важность.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Первое действие с полем товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$names = $product->getAttributeNames();
sort($names);
echo implode(",", $names), "
";
Ожидаемый результат
empty,id,p:amount,stock,xmlns:p

Значения остаются строками XML; бизнес-проверки выполняются отдельно.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
foreach ($product->getAttributeNames() as $name) {
    if (str_starts_with($name, "p:")) {
        echo $name, "
";
    }
}
Ожидаемый результат
p:amount

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::getAttribute.

Dom\Element::setAttributeИзменить остаток либо добавить поле экспорта, поручив DOM корректное XML-экранирование.

PHP 8.4+ · dom

Dom\Element::setAttribute(string $qualifiedName, string $value): void

Изменить остаток либо добавить поле экспорта, поручив DOM корректное XML-экранирование.

Аргумент / поведениеПолный контракт
qualifiedName / valueИмя и строковое значение. Значение передаётся как текст: вручную htmlspecialchars перед DOM не нужен, иначе появится двойное кодирование.
Возвратvoid в modern; DOMAttr|bool в legacy. В обоих случаях меняется элемент, а не возвращается его копия.
ОшибкиНедопустимое XML-имя → DOMException INVALID_CHARACTER_ERR; защищённая структура → NO_MODIFICATION_ALLOWED_ERR. Имя и данные проверяются раздельно.
ВыборДля namespace используйте setAttributeNS. Имена полей от клиента ограничивайте схемой, значения URL/HTML-поведения проверяйте по назначению.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Обновить остаток в XML товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->setAttribute("stock", "6");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="6"/>

setAttribute меняет выбранное поле. saveXml превращает изменённый узел в строку; другие атрибуты остаются.

Передать амперсанд как данные и увидеть XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->setAttribute("label", "Tea & coffee");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="4" label="Tea &amp; coffee"/>

Передаём обычный &, сериализатор записывает &. Ручной htmlspecialchars перед setAttribute дал бы двойное экранирование.

Связанные операции: Dom\Element::setAttributeNS.

Dom\Element::setAttributeNSДобавить сведения другой подсистемы, не смешивая одноимённые поля XML-контрактов.

PHP 8.4+ · dom

Dom\Element::setAttributeNS(?string $namespace, string $qualifiedName, string $value): void

Добавить сведения другой подсистемы, не смешивая одноимённые поля XML-контрактов.

Аргумент / поведениеПолный контракт
namespace / qualifiedName / valueURI (или null), имя с согласованным префиксом и строка значения. localName amount отличается от qualifiedName p:amount.
Возвратvoid в modern; void/false при ошибке в legacy с выключенной строгой обработкой.
ОшибкиINVALID_CHARACTER_ERR для имени; NAMESPACE_ERR для префикса без URI, неверных комбинаций xml/xmlns и зарезервированных URI. NUL и неверные типы тоже не являются допустимыми данными.
СериализацияDOM создаёт/сохраняет необходимые объявления префикса при сериализации. Не собирайте xmlns вручную строковыми заменами.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Изменить цену в словаре urn:priceСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:p="urn:price" sku="A1" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttributeNS("urn:price", "p:amount", "1500");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product xmlns:p="urn:price" sku="A1" p:amount="1500"/>

URI и локальное имя определяют поле; p остаётся краткой записью URI. На выходе есть объявление xmlns:p.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttributeNS("urn:delivery", "d:days", "2");
echo $product->getAttributeNS("urn:delivery", "days"), "
";
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
2
1200

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::setAttribute.

Dom\Element::removeAttributeУбрать необязательное или устаревшее поле из XML, сохранив остальные атрибуты.

PHP 8.4+ · dom

Dom\Element::removeAttribute(string $qualifiedName): void

Убрать необязательное или устаревшее поле из XML, сохранив остальные атрибуты.

Аргумент / поведениеПолный контракт
qualifiedNameИмя удаляемого атрибута; отсутствие не требует предварительного создания/замены.
Возвратvoid в modern; bool в legacy. Удаляет поле, а не присваивает пустую строку.
ОшибкаNO_MODIFICATION_ALLOWED_ERR для защищённого изменения; strictErrorChecking старого DOM влияет на форму сообщения/false.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Убрать остаток из публичного XML товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->removeAttribute("stock");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1"/>

Поля stock больше нет; это отличается от stock="". Так можно готовить отдельный публичный набор данных.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->removeAttribute("empty");
$product->removeAttribute("missing");
echo $product->getAttribute("id"), "
";
Ожидаемый результат
p1

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::removeAttributeNS.

Dom\Element::removeAttributeNSУдалить поле одной интеграции, не трогая похожие имена в другом пространстве.

PHP 8.4+ · dom

Dom\Element::removeAttributeNS(?string $namespace, string $localName): void

Удалить поле одной интеграции, не трогая похожие имена в другом пространстве.

Аргумент / поведениеПолный контракт
namespace / localNameURI и локальное имя. Используйте тот же URI, по которому поле читалось, а не его префикс.
Возвратvoid; отсутствие подходящего атрибута не создаёт ошибочное пустое поле.
ОтличиеУдаление объявления xmlns в legacy имеет особенности namespace-узлов. Здесь речь об обычном атрибуте в заданном URI.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Удалить цену выбранной интеграцииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:p="urn:price" sku="A1" p:amount="1200"/>');
$product = $document->documentElement;
$product->removeAttributeNS("urn:price", "amount");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product xmlns:p="urn:price" sku="A1"/>

Удалено само поле p:amount. Объявление xmlns:p осталось: отсутствие атрибута и очистка деклараций namespace — разные операции.

Применение к данным каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->removeAttributeNS("urn:unknown", "amount");
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
1200

Меняется или читается конкретный атрибут исходного элемента; весь XML повторно разбирать не требуется.

Связанные операции: Dom\Element::removeAttribute.

Dom\Element::toggleAttributeСогласовать булев атрибут со статусом товара без ручной пары set/remove.

PHP 8.4+ · dom

Dom\Element::toggleAttribute(string $qualifiedName, ?bool $force = null): bool

Согласовать булев атрибут со статусом товара без ручной пары set/remove.

Аргумент / поведениеПолный контракт
qualifiedName / force=nullnull переключает наличие; true обеспечивает наличие пустого атрибута, false удаляет. Если атрибут уже есть и force=true, значение сохраняется.
Возвратbool наличия ПОСЛЕ операции; false может означать успешное удаление, не ошибку.
ОшибкиНедопустимое имя → DOMException. В HTML имя нормализуется по правилам HTML; XML остаётся чувствительным к регистру.
ПовторяемостьДля повторной генерации используйте force с реальным состоянием, а не слепое переключение.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Два переключения отметки selectedСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$product->toggleAttribute("selected");
echo $document->saveXml($product), "\n";
$product->toggleAttribute("selected");
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="4" selected=""/>
<product sku="A1" stock="4"/>

Первый вызов добавил пустой атрибут, второй удалил его. Возврат bool сообщает наличие после операции, но результат изменения видно в XML.

Повторить синхронизацию статуса без случайного снятияСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$inStock = false;
$product->toggleAttribute('unavailable', !$inStock);
$product->toggleAttribute('unavailable', !$inStock);
echo $document->saveXml($product), "\n";
Ожидаемый результат
<product sku="A1" stock="4" unavailable=""/>

Второй аргумент force задаёт желаемое наличие. Повторная генерация с тем же состоянием сохраняет отметку. $inStock здесь отдельное демонстрационное состояние, а не вычисление из stock.

Связанные операции: Dom\Element::hasAttribute.

Dom\Element::getAttributeNodeПолучить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

PHP 8.4+ · dom

Dom\Element::getAttributeNode(string $qualifiedName): ?Dom\Attr

Получить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

Аргумент / поведениеПолный контракт
АргументыКвалифицированное имя атрибута
ВозвратDom\Attr|null в modern; DOMAttr|DOMNameSpaceNode|false в legacy (namespace-объявление представляется особым объектом). Отсутствие не равно пустому значению.
СвязьAttr.value связан с исходным элементом. Это не копия значения; для строки достаточно getAttribute.
НаследованиеAttr — узел, но не обычный дочерний Element; его ownerElement важнее parentNode.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Прочитать объект атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
echo $attribute->value, "
";
Ожидаемый результат
4

Стрелка value обращается к свойству Attr.

Изменить значение через полученный объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
$attribute->value = "8";
echo $product->getAttribute("stock"), "
";
Ожидаемый результат
8

И объект атрибута, и Element читают одно изменённое значение.

Связанные операции: Dom\Element::getAttribute, Dom\Element::removeAttributeNode.

Dom\Element::setAttributeNodeПрисоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

PHP 8.4+ · dom

Dom\Element::setAttributeNode(Dom\Attr $attr): ?Dom\Attr

Присоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

Аргумент / поведениеПолный контракт
$attrAttr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму.
ВозвратСтарый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом.
ВладениеModern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR).
ПрактикаДля обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Заменить существующее поле и прочитать староеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttribute("stock");
$attribute->value = "8";
$old = $product->setAttributeNode($attribute);
echo $old->value, ":", $attribute->value, "
";
Ожидаемый результат
4:8

Старый атрибут отсоединён, но его объект ещё доступен переменной old.

Повторно передать тот же объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttribute("stock");
$attribute->value = "8";
$product->setAttributeNode($attribute);
var_dump($product->setAttributeNode($attribute));
Ожидаемый результат
NULL

Повторное присоединение именно того же объекта не заменяет его новой копией.

Связанные операции: Dom\Element::setAttribute, Dom\Element::getAttributeNode.

Dom\Element::getAttributeNodeNSПолучить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

PHP 8.4+ · dom

Dom\Element::getAttributeNodeNS(?string $namespace, string $localName): ?Dom\Attr

Получить сам объект атрибута, когда нужно его состояние, namespace или передача другой операции DOM.

Аргумент / поведениеПолный контракт
АргументыURI + localName
ВозвратDom\Attr|null в modern; DOMAttr|DOMNameSpaceNode|false в legacy (namespace-объявление представляется особым объектом). Отсутствие не равно пустому значению.
СвязьAttr.value связан с исходным элементом. Это не копия значения; для строки достаточно getAttribute.
НаследованиеAttr — узел, но не обычный дочерний Element; его ownerElement важнее parentNode.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Прочитать объект атрибутаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNodeNS("urn:price", "amount");
echo $attribute->value, "
";
Ожидаемый результат
1200

Стрелка value обращается к свойству Attr.

Изменить значение через полученный объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNodeNS("urn:price", "amount");
$attribute->value = "8";
echo $product->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
8

И объект атрибута, и Element читают одно изменённое значение.

Связанные операции: Dom\Element::getAttributeNS, Dom\Element::removeAttributeNode.

Dom\Element::setAttributeNodeNSПрисоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

PHP 8.4+ · dom

Dom\Element::setAttributeNodeNS(Dom\Attr $attr): ?Dom\Attr

Присоединить заранее созданный Attr к товару, сохранив заменённое поле при необходимости.

Аргумент / поведениеПолный контракт
$attrAttr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму.
ВозвратСтарый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом.
ВладениеModern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR).
ПрактикаДля обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Заменить существующее поле и прочитать староеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttributeNS("urn:price", "p:amount");
$attribute->value = "8";
$old = $product->setAttributeNodeNS($attribute);
echo $old->value, ":", $attribute->value, "
";
Ожидаемый результат
1200:8

Старый атрибут отсоединён, но его объект ещё доступен переменной old.

Повторно передать тот же объектСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $document->createAttributeNS("urn:price", "p:amount");
$attribute->value = "8";
$product->setAttributeNodeNS($attribute);
var_dump($product->setAttributeNodeNS($attribute));
Ожидаемый результат
NULL

Повторное присоединение именно того же объекта не заменяет его новой копией.

Связанные операции: Dom\Element::setAttributeNS, Dom\Element::getAttributeNodeNS.

Dom\Element::removeAttributeNodeУдалить конкретный объект Attr, например после обработки поля, и оставить объект для журнала изменений.

PHP 8.4+ · dom

Dom\Element::removeAttributeNode(Dom\Attr $attr): Dom\Attr

Удалить конкретный объект Attr, например после обработки поля, и оставить объект для журнала изменений.

Аргумент / поведениеПолный контракт
$attrАтрибут, принадлежащий этому элементу; одно совпадающее имя у другого объекта недостаточно.
ВозвратУдалённый Attr; legacy может false при нестрогой ошибке. В старом тексте manual встречается true — фактический API возвращает объект.
ОшибкиNOT_FOUND_ERR для не принадлежащего элементу объекта; NO_MODIFICATION_ALLOWED_ERR для защищённого изменения.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Сохранить снятое значение и показать оставшийся товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="A1" stock="4"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode('stock');
$removed = $product->removeAttributeNode($attribute);
echo 'Снято поле stock=', $removed->value, "\n";
echo $document->saveXml($product), "\n";
Ожидаемый результат
Снято поле stock=4
<product sku="A1"/>

Возвращён снятый Attr, и его значение можно использовать в журнале изменений. Сам товар уже не содержит этого поля.

Снять поле один раз, проверяя его владельцаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("stock");
$product->removeAttributeNode($attribute);
if ($attribute->ownerElement === null) {
    echo "detached
";
}
Ожидаемый результат
detached

Для повторного вызова сначала проверяем ownerElement: повторно удалить уже отсоединённый Attr нельзя.

Связанные операции: Dom\Element::removeAttribute, Dom\Element::getAttributeNode.

Dom\Element::setIdAttributeОбъявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

PHP 8.4+ · dom

Dom\Element::setIdAttribute(string $qualifiedName, bool $isId): void

Объявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

Аргумент / поведениеПолный контракт
ПолеКвалифицированное имя существующего атрибута
$isIdtrue помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku.
Возврат / ошибкиvoid; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении.
ГраницаID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка.
Поле становится ключом DOM
  1. product sku="SKU-1"
  2. setIdAttribute*(…, true)
  3. document.getElementById("SKU-1") → этот product
Найти товар по отмеченному атрибутуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttribute("sku", "SKU-1");
$product->setIdAttribute("sku", true);
echo $document->getElementById("SKU-1")->nodeName, "
";
Ожидаемый результат
product

Помечается уже существующее поле со значением SKU-1.

Снять роль ID, сохранив артикул товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="SKU-1">Desk</product>');
$product = $document->documentElement;
$product->setIdAttribute("sku", true);
$product->setIdAttribute("sku", false);
echo $document->saveXml($product), "\n";
echo $document->getElementById("SKU-1") === null ? "Поле сохранено, поиск по ID отключён\n" : "Найдено\n";
Ожидаемый результат
<product sku="SKU-1">Desk</product>
Поле сохранено, поиск по ID отключён

false снимает пользовательскую пометку ID, а не удаляет атрибут или его текст. Видимый XML подтверждает сохранность поля.

Связанные операции: Dom\Document::getElementById, Dom\Attr::isId.

Dom\Element::setIdAttributeNSОбъявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

PHP 8.4+ · dom

Dom\Element::setIdAttributeNS(?string $namespace, string $qualifiedName, bool $isId): void

Объявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

Аргумент / поведениеПолный контракт
ПолеURI + localName существующего атрибута
$isIdtrue помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku.
Возврат / ошибкиvoid; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении.
ГраницаID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка.
Поле становится ключом DOM
  1. product sku="SKU-1"
  2. setIdAttribute*(…, true)
  3. document.getElementById("SKU-1") → этот product
Найти товар по отмеченному атрибутуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttributeNS("urn:key", "k:sku", "SKU-1");
$product->setIdAttributeNS("urn:key", "sku", true);
echo $document->getElementById("SKU-1")->nodeName, "
";
Ожидаемый результат
product

Помечается уже существующее поле со значением SKU-1.

Снять роль ID, сохранив артикул товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:k="urn:key" k:sku="SKU-1">Desk</product>');
$product = $document->documentElement;
$product->setIdAttributeNS("urn:key", "sku", true);
$product->setIdAttributeNS("urn:key", "sku", false);
echo $document->saveXml($product), "\n";
echo $document->getElementById("SKU-1") === null ? "Поле сохранено, поиск по ID отключён\n" : "Найдено\n";
Ожидаемый результат
<product xmlns:k="urn:key" k:sku="SKU-1">Desk</product>
Поле сохранено, поиск по ID отключён

false снимает пользовательскую пометку ID, а не удаляет атрибут или его текст. Видимый XML подтверждает сохранность поля.

Связанные операции: Dom\Document::getElementById, Dom\Attr::isId.

Dom\Element::setIdAttributeNodeОбъявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

PHP 8.4+ · dom

Dom\Element::setIdAttributeNode(Dom\Attr $attr, bool $isId): void

Объявить прикладное поле sku идентификатором DOM, чтобы затем находить товар через getElementById.

Аргумент / поведениеПолный контракт
ПолеAttr-объект этого элемента
$isIdtrue помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku.
Возврат / ошибкиvoid; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении.
ГраницаID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка.
Поле становится ключом DOM
  1. product sku="SKU-1"
  2. setIdAttribute*(…, true)
  3. document.getElementById("SKU-1") → этот product
Найти товар по отмеченному атрибутуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product id="p1" stock="4" empty="" xmlns:p="urn:price" p:amount="1200"/>');
$product = $document->documentElement;
$product->setAttribute("sku", "SKU-1");
$product->setIdAttributeNode($product->getAttributeNode("sku"), true);
echo $document->getElementById("SKU-1")->nodeName, "
";
Ожидаемый результат
product

Помечается уже существующее поле со значением SKU-1.

Снять роль ID, сохранив артикул товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="SKU-1">Desk</product>');
$product = $document->documentElement;
$product->setIdAttributeNode($product->getAttributeNode("sku"), true);
$product->setIdAttributeNode($product->getAttributeNode("sku"), false);
echo $document->saveXml($product), "\n";
echo $document->getElementById("SKU-1") === null ? "Поле сохранено, поиск по ID отключён\n" : "Найдено\n";
Ожидаемый результат
<product sku="SKU-1">Desk</product>
Поле сохранено, поиск по ID отключён

false снимает пользовательскую пометку ID, а не удаляет атрибут или его текст. Видимый XML подтверждает сохранность поля.

Связанные операции: Dom\Document::getElementById, Dom\Attr::isId.

Dom\Element::hasAttributesБыстро узнать, есть ли у элемента какие-либо атрибуты, перед формированием диагностического блока.

PHP 8.4+ · dom

Dom\Element::hasAttributes(): bool

Быстро узнать, есть ли у элемента какие-либо атрибуты, перед формированием диагностического блока.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; bool наличия хотя бы одного атрибута, включая объявления пространства имён modern DOM.
ГраницаНе проверяет обязательное поле: для этого hasAttribute/hasAttributeNS. Пустое значение всё равно считается существующим атрибутом.
Атрибут — подписанное поле элемента
  1. product содержит stock="4" и p:amount="1200"
  2. Имя выбирает поле; URI+localName отличает контракт
  3. Чтение даёт строку/Attr; изменение меняет тот же product
Не создавать пустой блок метаданныхСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product/>');
$product = $document->documentElement;
echo $product->hasAttributes() ? "Показываем поля товара\n" : "Полей нет: блок метаданных не нужен\n";
Ожидаемый результат
Полей нет: блок метаданных не нужен

Это проверка любых атрибутов. Для обязательного sku нужен отдельный hasAttribute и проверка непустого значения.

Пустое значение всё равно является полемСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product/>');
$product = $document->documentElement;
$product->setAttribute('comment', '');
if ($product->hasAttributes()) { echo $document->saveXml($product), "\n"; }
Ожидаемый результат
<product comment=""/>

Имя comment присутствует, хотя текст пуст. Поэтому hasAttributes возвращает true.

Связанные операции: Dom\Element::hasAttribute, Dom\Element::getAttributeNames.

DOMDocument::getElementsByTagNameНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 5+ · dom

DOMDocument::getElementsByTagName(string $qualifiedName): DOMNodeList

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыИмя тега либо "*". Legacy DOM фактически ищет по локальному имени и может включить p:product при запросе product. Modern сравнивает qualifiedName. Для точного URI используйте NS-вариант.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
foreach ($scope->getElementsByTagName("product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Tea
Coffee
Cake

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
$found = $scope->getElementsByTagName("product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group/></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: DOMDocument::getElementsByTagNameNS.

DOMDocument::getElementsByTagNameNSНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 5+ · dom

DOMDocument::getElementsByTagNameNS(?string $namespace, string $localName): DOMNodeList

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыURI/null/"*" для любого namespace и localName/"*" для любого локального имени.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
foreach ($scope->getElementsByTagNameNS("urn:partner", "product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Cake

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
$found = $scope->getElementsByTagNameNS("urn:partner", "product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: DOMDocument::getElementsByTagName.

DOMDocument::getElementByIdНайти одну конкретную сущность DOM по идентификатору, а не повторно перебирать весь каталог вручную.

PHP 5+ · dom

DOMDocument::getElementById(string $elementId): ?DOMElement

Найти одну конкретную сущность DOM по идентификатору, а не повторно перебирать весь каталог вручную.

Аргумент / поведениеПолный контракт
$elementIdЗначение ID, например SKU-1, а не имя атрибута sku.
ВозвратElement либо null. В modern обычный id имеет специальную семантику; legacy XML не считает произвольное имя id типом ID без объявления/пометки.
ПодготовкаДля своего XML-поля используйте setIdAttribute/NS/Node. DTD может определить тип ID; его загрузка/валидация требует контролируемого источника.
ПравилаИдентификаторы должны быть уникальны; неоднозначность дублей не следует использовать как контракт. Пустое/отсутствующее совпадение обрабатывайте явно.
Индекс идентификаторов
  1. product sku="SKU-1"
  2. sku явно помечено типом ID
  3. getElementById("SKU-1") → один объект
Найти товар по известному IDСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product sku="SKU-1">Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$product->setIdAttribute("sku", true);
echo $document->getElementById("SKU-1")->textContent, "
";
Ожидаемый результат
Tea

Пометка нужна для пользовательского поля sku, независимо от семейства API.

Явно обработать удалённый/неизвестный товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
$found = $document->getElementById("unknown");
echo $found === null ? "not found
" : $found->textContent;
Ожидаемый результат
not found

Отсутствие узла — нормальный результат поиска, не повод обращаться к его свойствам.

Связанные операции: DOMElement::setIdAttribute, DOMDocument::getElementsByTagName.

DOMElement::getElementsByTagNameНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 5+ · dom

DOMElement::getElementsByTagName(string $qualifiedName): DOMNodeList

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыИмя тега либо "*". Legacy DOM фактически ищет по локальному имени и может включить p:product при запросе product. Modern сравнивает qualifiedName. Для точного URI используйте NS-вариант.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
foreach ($scope->getElementsByTagName("product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Tea
Coffee
Cake

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
$found = $scope->getElementsByTagName("product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group/></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: DOMElement::getElementsByTagNameNS.

DOMElement::getElementsByTagNameNSНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 5+ · dom

DOMElement::getElementsByTagNameNS(?string $namespace, string $localName): DOMNodeList

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыURI/null/"*" для любого namespace и localName/"*" для любого локального имени.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
foreach ($scope->getElementsByTagNameNS("urn:partner", "product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Cake

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
$found = $scope->getElementsByTagNameNS("urn:partner", "product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: DOMElement::getElementsByTagName.

Dom\Document::getElementsByTagNameНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 8.4+ · dom

Dom\Document::getElementsByTagName(string $qualifiedName): Dom\HTMLCollection

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыИмя тега либо "*". Legacy DOM фактически ищет по локальному имени и может включить p:product при запросе product. Modern сравнивает qualifiedName. Для точного URI используйте NS-вариант.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
foreach ($scope->getElementsByTagName("product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Tea
Coffee

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
$found = $scope->getElementsByTagName("product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group/><p:product xmlns:p="urn:partner">Cake</p:product></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: Dom\Document::getElementsByTagNameNS.

Dom\Document::getElementsByTagNameNSНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 8.4+ · dom

Dom\Document::getElementsByTagNameNS(?string $namespace, string $localName): Dom\HTMLCollection

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыURI/null/"*" для любого namespace и localName/"*" для любого локального имени.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
foreach ($scope->getElementsByTagNameNS("urn:partner", "product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Cake

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
$found = $scope->getElementsByTagNameNS("urn:partner", "product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: Dom\Document::getElementsByTagName.

Dom\Document::getElementsByClassNameНайти элементы по CSS-классам, например все карточки распродажи в подготовленном HTML/XML.

PHP 8.5+ · dom

Dom\Document::getElementsByClassName(string $classNames): Dom\HTMLCollection

Найти элементы по CSS-классам, например все карточки распродажи в подготовленном HTML/XML.

Аргумент / поведениеПолный контракт
$classNamesСтрока из одного или нескольких токенов, разделённых ASCII-пробелами. Несколько имён означают наличие всех классов, не «любого из них».
ВозвратЖивая HTMLCollection подходящих потомков в порядке документа. Пустая/пробельная строка даёт пустую коллекцию.
Версия / границыPHP 8.5+. Это сравнение токенов class, не выполнение CSS. HTML quirks mode может влиять на сравнение регистра; для воспроизводимости создавайте корректный HTML с doctype.
Пересечение CSS-меток
  1. Tea: card sale; Coffee: card
  2. getElementsByClassName("card sale")
  3. Выбрана только Tea
Найти карточку с двумя меткамиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="card sale">Tea</product><product class="card">Coffee</product></catalog>');
$scope = $document;
echo $scope->getElementsByClassName("card sale")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Два слова — условие AND.

Увидеть живое обновление при снятии классаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="card sale">Tea</product><product class="card">Coffee</product></catalog>');
$scope = $document;
$sale = $scope->getElementsByClassName("sale");
$sale->item(0)->classList->remove("sale");
echo $sale->length, "
";
Ожидаемый результат
0

Изменение атрибута class отражается в уже полученной коллекции.

Связанные операции: Dom\Document::querySelectorAll, Dom\TokenList::remove.

Dom\Document::getElementByIdНайти одну конкретную сущность DOM по идентификатору, а не повторно перебирать весь каталог вручную.

PHP 8.4+ · dom

Dom\Document::getElementById(string $elementId): ?Dom\Element

Найти одну конкретную сущность DOM по идентификатору, а не повторно перебирать весь каталог вручную.

Аргумент / поведениеПолный контракт
$elementIdЗначение ID, например SKU-1, а не имя атрибута sku.
ВозвратElement либо null. В modern обычный id имеет специальную семантику; legacy XML не считает произвольное имя id типом ID без объявления/пометки.
ПодготовкаДля своего XML-поля используйте setIdAttribute/NS/Node. DTD может определить тип ID; его загрузка/валидация требует контролируемого источника.
ПравилаИдентификаторы должны быть уникальны; неоднозначность дублей не следует использовать как контракт. Пустое/отсутствующее совпадение обрабатывайте явно.
Индекс идентификаторов
  1. product sku="SKU-1"
  2. sku явно помечено типом ID
  3. getElementById("SKU-1") → один объект
Найти товар по известному IDСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product sku="SKU-1">Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$product->setIdAttribute("sku", true);
echo $document->getElementById("SKU-1")->textContent, "
";
Ожидаемый результат
Tea

Пометка нужна для пользовательского поля sku, независимо от семейства API.

Явно обработать удалённый/неизвестный товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document;
$found = $document->getElementById("unknown");
echo $found === null ? "not found
" : $found->textContent;
Ожидаемый результат
not found

Отсутствие узла — нормальный результат поиска, не повод обращаться к его свойствам.

Связанные операции: Dom\Element::setIdAttribute, Dom\Document::getElementsByTagName.

Dom\Element::getElementsByTagNameНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 8.4+ · dom

Dom\Element::getElementsByTagName(string $qualifiedName): Dom\HTMLCollection

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыИмя тега либо "*". Legacy DOM фактически ищет по локальному имени и может включить p:product при запросе product. Modern сравнивает qualifiedName. Для точного URI используйте NS-вариант.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
foreach ($scope->getElementsByTagName("product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Tea
Coffee

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
$found = $scope->getElementsByTagName("product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group/><p:product xmlns:p="urn:partner">Cake</p:product></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: Dom\Element::getElementsByTagNameNS.

Dom\Element::getElementsByTagNameNSНайти позиции XML-каталога внутри выбранного документа или ветки.

PHP 8.4+ · dom

Dom\Element::getElementsByTagNameNS(?string $namespace, string $localName): Dom\HTMLCollection

Найти позиции XML-каталога внутри выбранного документа или ветки.

Аргумент / поведениеПолный контракт
АргументыURI/null/"*" для любого namespace и localName/"*" для любого локального имени.
ОбластьВсе потомки в порядке документа; сам Element, на котором вызван метод, не включается. У Document поиск охватывает документ, включая корень при совпадении.
ВозвратЖивая Dom\HTMLCollection (modern) либо DOMNodeList (legacy). Пустой результат — коллекция с length=0, не false/null.
Стоимость / выборПоиск может обходить большую ветку. Ограничивайте scope известной частью дерева. Для условий по атрибутам — XPath/CSS; для очень большого файла — XMLReader.
Поиск потомков
  1. scope → catalog → group → product Tea
  2. Поиск видит вложенность любой глубины, а не только прямых детей
  3. Получаем живую коллекцию подходящих Element
Найти товары выбранного имениСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
foreach ($scope->getElementsByTagNameNS("urn:partner", "product") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Cake

В modern product и p:product различаются; legacy поиск по локальному имени включает оба. NS-вариант устраняет эту неоднозначность.

Увидеть каталог после удаления выбранных товаровСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product><p:product xmlns:p="urn:partner">Cake</p:product></catalog>');
$scope = $document->documentElement;
$found = $scope->getElementsByTagNameNS("urn:partner", "product");
$snapshot = iterator_to_array($found, false);
foreach ($snapshot as $product) {
    $product->parentNode->removeChild($product);
}
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><group><product sku="p1">Tea</product></group><product sku="p2">Coffee</product></catalog>

Сначала получили отдельный массив ссылок на выбранные узлы, затем удалили их. Так живой список не пропускает соседнюю запись. На выходе виден весь оставшийся каталог, включая неподходящие namespace и пустую группу.

Связанные операции: Dom\Element::getElementsByTagName.

Dom\Element::getElementsByClassNameНайти элементы по CSS-классам, например все карточки распродажи в подготовленном HTML/XML.

PHP 8.5+ · dom

Dom\Element::getElementsByClassName(string $classNames): Dom\HTMLCollection

Найти элементы по CSS-классам, например все карточки распродажи в подготовленном HTML/XML.

Аргумент / поведениеПолный контракт
$classNamesСтрока из одного или нескольких токенов, разделённых ASCII-пробелами. Несколько имён означают наличие всех классов, не «любого из них».
ВозвратЖивая HTMLCollection подходящих потомков в порядке документа. Пустая/пробельная строка даёт пустую коллекцию.
Версия / границыPHP 8.5+. Это сравнение токенов class, не выполнение CSS. HTML quirks mode может влиять на сравнение регистра; для воспроизводимости создавайте корректный HTML с doctype.
Пересечение CSS-меток
  1. Tea: card sale; Coffee: card
  2. getElementsByClassName("card sale")
  3. Выбрана только Tea
Найти карточку с двумя меткамиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="card sale">Tea</product><product class="card">Coffee</product></catalog>');
$scope = $document->documentElement;
echo $scope->getElementsByClassName("card sale")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Два слова — условие AND.

Увидеть живое обновление при снятии классаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="card sale">Tea</product><product class="card">Coffee</product></catalog>');
$scope = $document->documentElement;
$sale = $scope->getElementsByClassName("sale");
$sale->item(0)->classList->remove("sale");
echo $sale->length, "
";
Ожидаемый результат
0

Изменение атрибута class отражается в уже полученной коллекции.

Связанные операции: Dom\Element::querySelectorAll, Dom\TokenList::remove.

Dom\ParentNode::querySelectorВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\ParentNode::querySelector(string $selectors): ?Dom\Element

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратПервый совпавший Dom\Element в порядке документа либо null.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelector("product.sale");
echo $found->textContent, "
";
Ожидаемый результат
Tea

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Принять решение при отсутствии подходящего товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelector("product.missing");
echo $found === null ? "Товара с меткой missing нет: карточку не создаём\n" : $found->textContent;
Ожидаемый результат
Товара с меткой missing нет: карточку не создаём

null означает отсутствие узла. Проверяем его до чтения textContent. Это нормальная ветка каталога, а не пустой товар.

Связанные операции: Dom\ParentNode::querySelectorAll, Dom\Element::matches.

Dom\ParentNode::querySelectorAllВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\ParentNode::querySelectorAll(string $selectors): Dom\NodeList

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратСтатический Dom\NodeList всех совпадений, включая пустой список.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelectorAll("product.sale");
echo $found->item(0)->textContent, ":", $found->length, "
";
Ожидаемый результат
Tea:1

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Различить снимок результата и состояние дереваСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelectorAll("product.sale");
$found->item(0)->removeAttribute("class");
echo $found->length, ":", $scope->querySelectorAll("product.sale")->length, "
";
Ожидаемый результат
1:0

Повторный запрос видит новое состояние дерева.

Связанные операции: Dom\ParentNode::querySelector, Dom\Element::matches.

Dom\Document::querySelectorВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\Document::querySelector(string $selectors): ?Dom\Element

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратПервый совпавший Dom\Element в порядке документа либо null.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document;
$found = $scope->querySelector("product.sale");
echo $found->textContent, "
";
Ожидаемый результат
Tea

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Принять решение при отсутствии подходящего товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document;
$found = $scope->querySelector("product.missing");
echo $found === null ? "Товара с меткой missing нет: карточку не создаём\n" : $found->textContent;
Ожидаемый результат
Товара с меткой missing нет: карточку не создаём

null означает отсутствие узла. Проверяем его до чтения textContent. Это нормальная ветка каталога, а не пустой товар.

Связанные операции: Dom\Document::querySelectorAll, Dom\Element::matches.

Dom\Document::querySelectorAllВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\Document::querySelectorAll(string $selectors): Dom\NodeList

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратСтатический Dom\NodeList всех совпадений, включая пустой список.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document;
$found = $scope->querySelectorAll("product.sale");
echo $found->item(0)->textContent, ":", $found->length, "
";
Ожидаемый результат
Tea:1

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Различить снимок результата и состояние дереваСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document;
$found = $scope->querySelectorAll("product.sale");
$found->item(0)->removeAttribute("class");
echo $found->length, ":", $scope->querySelectorAll("product.sale")->length, "
";
Ожидаемый результат
1:0

Повторный запрос видит новое состояние дерева.

Связанные операции: Dom\Document::querySelector, Dom\Element::matches.

Dom\Element::querySelectorВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\Element::querySelector(string $selectors): ?Dom\Element

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратПервый совпавший Dom\Element в порядке документа либо null.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelector("product.sale");
echo $found->textContent, "
";
Ожидаемый результат
Tea

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Принять решение при отсутствии подходящего товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelector("product.missing");
echo $found === null ? "Товара с меткой missing нет: карточку не создаём\n" : $found->textContent;
Ожидаемый результат
Товара с меткой missing нет: карточку не создаём

null означает отсутствие узла. Проверяем его до чтения textContent. Это нормальная ветка каталога, а не пустой товар.

Связанные операции: Dom\Element::querySelectorAll, Dom\Element::matches.

Dom\Element::querySelectorAllВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\Element::querySelectorAll(string $selectors): Dom\NodeList

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратСтатический Dom\NodeList всех совпадений, включая пустой список.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelectorAll("product.sale");
echo $found->item(0)->textContent, ":", $found->length, "
";
Ожидаемый результат
Tea:1

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Различить снимок результата и состояние дереваСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->documentElement;
$found = $scope->querySelectorAll("product.sale");
$found->item(0)->removeAttribute("class");
echo $found->length, ":", $scope->querySelectorAll("product.sale")->length, "
";
Ожидаемый результат
1:0

Повторный запрос видит новое состояние дерева.

Связанные операции: Dom\Element::querySelector, Dom\Element::matches.

Dom\DocumentFragment::querySelectorВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\DocumentFragment::querySelector(string $selectors): ?Dom\Element

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратПервый совпавший Dom\Element в порядке документа либо null.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->createDocumentFragment();
$scope->append($document->documentElement);
$found = $scope->querySelector("product.sale");
echo $found->textContent, "
";
Ожидаемый результат
Tea

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Принять решение при отсутствии подходящего товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->createDocumentFragment();
$scope->append($document->documentElement);
$found = $scope->querySelector("product.missing");
echo $found === null ? "Товара с меткой missing нет: карточку не создаём\n" : $found->textContent;
Ожидаемый результат
Товара с меткой missing нет: карточку не создаём

null означает отсутствие узла. Проверяем его до чтения textContent. Это нормальная ветка каталога, а не пустой товар.

Связанные операции: Dom\DocumentFragment::querySelectorAll, Dom\Element::matches.

Dom\DocumentFragment::querySelectorAllВыбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

PHP 8.4+ · dom

Dom\DocumentFragment::querySelectorAll(string $selectors): Dom\NodeList

Выбрать элементы по знакомому CSS-условию: класс, атрибут или структура каталога.

Аргумент / поведениеПолный контракт
$selectorsСтрока CSS-селектора, например product.sale или product[stock="4"]. Это не XPath и не текст для eval. Значения из внешних данных не склеивайте с CSS без корректного экранирования/ограничения.
ВозвратСтатический Dom\NodeList всех совпадений, включая пустой список.
ОбластьПотомки текущего Document/Element/DocumentFragment; для относительного ограничения используйте :scope. Условия сложного селектора могут обращаться к предкам вне ветки; это не автоматический sandbox CSS.
ОшибкиDOMException SYNTAX_ERR при неправильном селекторе; ValueError для неподдержанного движком селектора. Не обещаем выполнение любого браузерного CSS/псевдосостояния.
СтатичностьquerySelectorAll фиксирует набор ссылок в момент запроса, но не клонирует объекты. После мутации узел может остаться в результате, уже не удовлетворяя условию.
ИнтерфейсParentNode задаёт контракт для Document, Element и DocumentFragment; его нельзя создать через new.
Селектор → результат
  1. product.sale: выбрать товар с CSS-меткой sale
  2. PHP выполняет поиск в дереве на сервере
  3. Результат — объекты узлов; JavaScript/стили не исполняются
Прочитать совпавший товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->createDocumentFragment();
$scope->append($document->documentElement);
$found = $scope->querySelectorAll("product.sale");
echo $found->item(0)->textContent, ":", $found->length, "
";
Ожидаемый результат
Tea:1

Точка перед sale обозначает класс; квадратные скобки в другом условии обозначали бы атрибут.

Различить снимок результата и состояние дереваСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product class="sale" stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$scope = $document->createDocumentFragment();
$scope->append($document->documentElement);
$found = $scope->querySelectorAll("product.sale");
$found->item(0)->removeAttribute("class");
echo $found->length, ":", $scope->querySelectorAll("product.sale")->length, "
";
Ожидаемый результат
1:0

Повторный запрос видит новое состояние дерева.

Связанные операции: Dom\DocumentFragment::querySelector, Dom\Element::matches.

Dom\Element::matchesПроверить уже известный элемент по CSS-условию, не искать его повторно во всём документе.

PHP 8.4+ · dom

Dom\Element::matches(string $selectors): bool

Проверить уже известный элемент по CSS-условию, не искать его повторно во всём документе.

Аргумент / поведениеПолный контракт
$selectorsCSS-селектор/список селекторов; проверяется сам Element, не его дети.
Возвратbool. Неверный синтаксис → DOMException SYNTAX_ERR, неподдержанное условие → ValueError.
ПрименениеУдобно в обходе смешанных узлов, когда нужно отличить sale-товар от других. Для нахождения предка — closest.
Проверяется один объект
  1. product = уже найденный Tea
  2. matches("product.sale")
  3. true: совпал сам product
Проверить метку конкретного товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group class="department"><product class="sale">Tea</product></group></catalog>');
$product = $document->getElementsByTagName("product")->item(0);
var_dump($product->matches("product.sale"));
var_dump($product->matches("group"));
Ожидаемый результат
bool(true)
bool(false)

matches не поднимается к group.

Изменить только подходящую карточкуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group class="department"><product class="sale">Tea</product></group></catalog>');
$product = $document->getElementsByTagName("product")->item(0);
if ($product->matches(".sale")) {
    $product->setAttribute("discount", "10");
}
echo $product->getAttribute("discount"), "
";
Ожидаемый результат
10

Проверка CSS-метки выбирает ветку логики; размер скидки определяется вашим правилом, не CSS.

Связанные операции: Dom\Element::closest, Dom\Element::querySelector.

Dom\Element::closestНайти раздел/контейнер, которому принадлежит выбранный узел, например группу товара.

PHP 8.4+ · dom

Dom\Element::closest(string $selectors): ?Dom\Element

Найти раздел/контейнер, которому принадлежит выбранный узел, например группу товара.

Аргумент / поведениеПолный контракт
$selectorsУсловие проверяется сначала для текущего Element, затем для родителей вверх до корня.
ВозвратПервый совпавший Element либо null. Может вернуть сам объект, если он подходит.
ОшибкиНекорректный CSS → SYNTAX_ERR; неподдержанное выражение → ValueError. Это поиск родителей, не братьев/детей.
Подъём по дереву
  1. Tea product не group
  2. Родитель group.department совпал
  3. Возвращён group; catalog выше уже не проверяется
Найти родительскую группуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group class="department"><product class="sale">Tea</product></group></catalog>');
$product = $document->getElementsByTagName("product")->item(0);
echo $product->closest("group")->nodeName, "
";
Ожидаемый результат
group

Не нужно вручную писать while с parentElement для простого CSS-условия.

Увидеть совпадение себя и отсутствие предкаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><group class="department"><product class="sale">Tea</product></group></catalog>');
$product = $document->getElementsByTagName("product")->item(0);
var_dump($product->closest("product") === $product);
var_dump($product->closest("warehouse"));
Ожидаемый результат
bool(true)
NULL

closest включает себя, в отличие от обхода, начинающегося только с parentElement.

Связанные операции: Dom\Element::matches, Dom\Element::querySelector.

Dom\Element::getInScopeNamespacesИзучить контекст пространств имён перед диагностикой XML, переносом ветки или построением namespace-aware запроса.

PHP 8.4+ · dom

Dom\Element::getInScopeNamespaces(): array

Изучить контекст пространств имён перед диагностикой XML, переносом ветки или построением namespace-aware запроса.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; array<Dom\NamespaceInfo>. Каждая запись содержит readonly prefix, namespaceURI, element.
ОхватОбъявления, действующие на текущем элементе, с учётом наследования и переопределения префиксов предками.
Значенияprefix=null обозначает default namespace, namespaceURI может быть null при отмене объявления. Не считайте результат уникальным словарём по URI.
Цена / выборДля одного известного префикса достаточно lookupNamespaceURI. Обход всех потомков может быть значительно дороже и выдаёт отдельные контексты.
Объявление и область действия
  1. catalog объявляет p → urn:price
  2. product наследует p и добавляет d → urn:delivery
  3. NamespaceInfo связывает префикс, URI и рассматриваемый element
Найти префикс доставки в контексте товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns:p="urn:price"><product xmlns:d="urn:delivery"/></catalog>');
$product = $document->documentElement->firstElementChild;
foreach ($product->getInScopeNamespaces() as $namespace) {
    if ($namespace->prefix === "d") {
        echo $namespace->namespaceURI, "
";
    }
}
Ожидаемый результат
urn:delivery

Запись говорит о namespace-контексте, а не о сетевом URL, который надо загрузить.

Показать унаследованный контекст ценыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns:p="urn:price"><product xmlns:d="urn:delivery"/></catalog>');
$product = $document->documentElement->firstElementChild;
foreach ($product->getInScopeNamespaces() as $namespace) {
    if ($namespace->prefix === "p") {
        echo $namespace->namespaceURI, ":", $namespace->element->nodeName, "
";
    }
}
Ожидаемый результат
urn:price:product

element — узел, для которого вычислен контекст, а не обязательно место исходного xmlns-объявления.

Связанные операции: Dom\Node::lookupNamespaceURI, Dom\Element::getAttributeNS.

Dom\Element::getDescendantNamespacesИзучить контекст пространств имён перед диагностикой XML, переносом ветки или построением namespace-aware запроса.

PHP 8.4+ · dom

Dom\Element::getDescendantNamespaces(): array

Изучить контекст пространств имён перед диагностикой XML, переносом ветки или построением namespace-aware запроса.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; array<Dom\NamespaceInfo>. Каждая запись содержит readonly prefix, namespaceURI, element.
ОхватКонтекст текущего элемента и каждого его потомка-элемента; одинаковый префикс/URI может встретиться много раз с разными element.
Значенияprefix=null обозначает default namespace, namespaceURI может быть null при отмене объявления. Не считайте результат уникальным словарём по URI.
Цена / выборДля одного известного префикса достаточно lookupNamespaceURI. Обход всех потомков может быть значительно дороже и выдаёт отдельные контексты.
Объявление и область действия
  1. catalog объявляет p → urn:price
  2. product наследует p и добавляет d → urn:delivery
  3. NamespaceInfo связывает префикс, URI и рассматриваемый element
Найти префикс доставки в контексте товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns:p="urn:price"><product xmlns:d="urn:delivery"/></catalog>');
$product = $document->documentElement->firstElementChild;
foreach ($product->getDescendantNamespaces() as $namespace) {
    if ($namespace->prefix === "d") {
        echo $namespace->namespaceURI, "
";
    }
}
Ожидаемый результат
urn:delivery

Запись говорит о namespace-контексте, а не о сетевом URL, который надо загрузить.

Показать унаследованный контекст ценыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns:p="urn:price"><product xmlns:d="urn:delivery"/></catalog>');
$product = $document->documentElement->firstElementChild;
foreach ($product->getDescendantNamespaces() as $namespace) {
    if ($namespace->prefix === "p") {
        echo $namespace->namespaceURI, ":", $namespace->element->nodeName, "
";
    }
}
Ожидаемый результат
urn:price:product

element — узел, для которого вычислен контекст, а не обязательно место исходного xmlns-объявления.

Связанные операции: Dom\Node::lookupNamespaceURI, Dom\Element::getAttributeNS.

DOMDocument::createElementСоздать поле/элемент XML-экспорта, не собирая открывающие и закрывающие теги вручную.

PHP 5+ · dom

DOMDocument::createElement(string $localName, string $value = ""): DOMElement|false

Создать поле/элемент XML-экспорта, не собирая открывающие и закрывающие теги вручную.

Аргумент / поведениеПолный контракт
localNameИмя тега; нельзя использовать недопустимые XML-символы. Modern HTMLDocument создаёт HTML-элемент в HTML namespace; XMLDocument создаёт обычный XML-элемент.
valueВ modern такого аргумента нет: создайте Text или присвойте textContent. В legacy необязательный value НЕ полностью экранирует &: для произвольного текста тоже используйте createTextNode.
Возврат / ошибкиElement; legacy также false при нестрогой ошибке. INVALID_CHARACTER_ERR для имени. Создание не вставляет узел в документ.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createElement("product");
$document->documentElement->appendChild($node);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><product/></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createElement("title");
$node->appendChild($document->createTextNode("Tea & coffee"));
echo $document->saveXml($node), "
";
Ожидаемый результат
<title>Tea &amp; coffee</title>

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createTextNode.

DOMDocument::createElementNSСоздать элемент конкретного XML-контракта с правильным пространством имён.

PHP 5+ · dom

DOMDocument::createElementNS(?string $namespace, string $qualifiedName, string $value = ""): DOMElement|false

Создать элемент конкретного XML-контракта с правильным пространством имён.

Аргумент / поведениеПолный контракт
namespace / qualifiedNameURI/null и имя вида p:product. Префикс требует URI; xml/xmlns имеют зарезервированные значения.
valueModern принимает только namespace и qualifiedName; legacy имеет дополнительный value с теми же ограничениями экранирования, что createElement.
Возврат / ошибкиElement (legacy может false); INVALID_CHARACTER_ERR/NAMESPACE_ERR при неверном имени/связи префикса и URI.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createElementNS("urn:price", "p:amount");
echo $node->namespaceURI, ":", $node->localName, "
";
Ожидаемый результат
urn:price:amount

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createElementNS("urn:price", "p:amount");
$node->appendChild($document->createTextNode("1200"));
$document->documentElement->appendChild($node);
echo $document->getElementsByTagNameNS("urn:price", "amount")->item(0)->textContent, "
";
Ожидаемый результат
1200

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createElement.

DOMDocument::createAttributeПодготовить объект поля XML перед присоединением к элементу.

PHP 5+ · dom

DOMDocument::createAttribute(string $localName): DOMAttr|false

Подготовить объект поля XML перед присоединением к элементу.

Аргумент / поведениеПолный контракт
localNameИмя нового атрибута. Недопустимое имя → INVALID_CHARACTER_ERR.
ВозвратНовый Attr с пустым значением и ownerElement=null; legacy допускает false при нестрогой ошибке.
ДальшеПрисвойте value и передайте setAttributeNode. Для простого изменения одной строки короче Element::setAttribute.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$attribute = $document->createAttribute("stock");
$attribute->value = "4";
$document->documentElement->setAttributeNode($attribute);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog stock="4"/>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$attribute = $document->createAttribute("currency");
$attribute->value = "RUB";
var_dump($attribute->ownerElement);
echo $attribute->value, "
";
Ожидаемый результат
NULL
RUB

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createAttributeNS.

DOMDocument::createAttributeNSСоздать атрибут внешней схемы, сохранив URI отдельно от его короткого префикса.

PHP 5+ · dom

DOMDocument::createAttributeNS(?string $namespace, string $qualifiedName): DOMAttr|false

Создать атрибут внешней схемы, сохранив URI отдельно от его короткого префикса.

Аргумент / поведениеПолный контракт
namespace / qualifiedNameURI/null и имя с префиксом. NAMESPACE_ERR при противоречивой комбинации; INVALID_CHARACTER_ERR при неверном имени.
ВозвратСвободный Attr; legacy может false. В старом API для корректной привязки namespace документ должен иметь корневой элемент; наши примеры его создают заранее.
ЗначениеЗадаётся через Attr.value и прикрепляется setAttributeNodeNS. xmlns не является произвольной пользовательской областью.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$attribute = $document->createAttributeNS("urn:price", "p:amount");
$attribute->value = "1200";
$document->documentElement->setAttributeNodeNS($attribute);
echo $document->documentElement->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
1200

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$attribute = $document->createAttributeNS("urn:delivery", "d:days");
echo $attribute->prefix, ":", $attribute->localName, ":", $attribute->namespaceURI, "
";
Ожидаемый результат
d:days:urn:delivery

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createAttribute.

DOMDocument::createTextNodeПоместить название товара или пользовательский текст в XML без превращения специальных символов в разметку.

PHP 5+ · dom

DOMDocument::createTextNode(string $data): DOMText

Поместить название товара или пользовательский текст в XML без превращения специальных символов в разметку.

Аргумент / поведениеПолный контракт
dataСтрока текста в UTF-8. Передаётся как данные, а не XML-фрагмент. Не делайте htmlspecialchars заранее.
ВозвратText с ownerDocument текущего документа. Сериализатор экранирует &, < по правилам формата.</td>
ГраницаКорректное XML-экранирование не делает произвольный URL безопасным и не разрешает данные в JavaScript/CSS-контексте. Для атрибутов используйте их отдельные операции.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$text = $document->createTextNode("Tea & coffee");
$document->documentElement->appendChild($text);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>Tea &amp; coffee</catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$text = $document->createTextNode("<strong>Sale</strong>");
echo $text->data, "
";
echo $document->saveXml($text), "
";
Ожидаемый результат
<strong>Sale</strong>
&lt;strong&gt;Sale&lt;/strong&gt;

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createElement.

DOMDocument::createCDATASectionПредставить текст как CDATA, когда получатель XML ожидает такую сериализацию описания.

PHP 5+ · dom

DOMDocument::createCDATASection(string $data): DOMCdataSection|false

Представить текст как CDATA, когда получатель XML ожидает такую сериализацию описания.

Аргумент / поведениеПолный контракт
dataСтрока текста; последовательность ]]> завершает CDATA и недопустима внутри одной modern секции (INVALID_CHARACTER_ERR).
ВозвратCDATASection. Modern HTMLDocument запрещает CDATA (NOT_SUPPORTED_ERR); эта операция для XMLDocument.
СмыслCDATA не шифрует и не санитизирует HTML. После XML-разбора это снова строка; при вставке её в браузер нужна отдельная политика HTML.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createCDATASection("<b>Sale</b>");
$document->documentElement->appendChild($node);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><![CDATA[<b>Sale</b>]]></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = $document->createCDATASection("Tea & coffee");
echo $node->data, "
";
echo $node->length, "
";
Ожидаемый результат
Tea & coffee
12

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createTextNode.

DOMDocument::createCommentДобавить пояснение в генерируемый XML-файл для человека, который будет его просматривать.

PHP 5+ · dom

DOMDocument::createComment(string $data): DOMComment

Добавить пояснение в генерируемый XML-файл для человека, который будет его просматривать.

Аргумент / поведениеПолный контракт
dataТекст комментария. Не помещайте секреты: комментарии тоже выходят в сериализованный файл. Для XML избегайте -- и завершающего -, даже если создание узла это допускает.
ВозвратComment, ещё не вставленный в документ.
ГраницаКомментарий не является обычным текстом элемента и не задаёт настройку парсера. За валидность финальной XML-разметки отвечает весь процесс генерации.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$comment = $document->createComment("Generated catalog");
$document->documentElement->appendChild($comment);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><!--Generated catalog--></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$comment = $document->createComment("Updated daily");
echo $comment->data, "
";
var_dump($comment->parentNode);
Ожидаемый результат
Updated daily
NULL

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createTextNode.

DOMDocument::createProcessingInstructionДобавить инструкцию для специализированного XML-получателя, например связь с локальной таблицей стилей.

PHP 5+ · dom

DOMDocument::createProcessingInstruction(string $target, string $data = ""): DOMProcessingInstruction|false

Добавить инструкцию для специализированного XML-получателя, например связь с локальной таблицей стилей.

Аргумент / поведениеПолный контракт
target / dataXML-имя назначения и строка данных. Legacy data необязательна (пустая по умолчанию), modern требует её явно. target xml зарезервирован, data не должна содержать ?>.
Возврат / ошибкиProcessingInstruction; некорректное имя/данные → INVALID_CHARACTER_ERR (legacy в отдельных случаях warning/false).
ГраницаСоздание xml-stylesheet не запускает XSLT в PHP. Обработку выполняет отдельный потребитель. Не принимайте произвольные пути/URL инструкции от клиента.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$pi = $document->createProcessingInstruction("report", "version=1");
$document->insertBefore($pi, $document->documentElement);
echo $document->saveXml($pi), "
";
Ожидаемый результат
<?report version=1?>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$pi = $document->createProcessingInstruction("xml-stylesheet", 'type="text/xsl" href="report.xsl"');
echo $pi->target, "
";
echo $pi->data, "
";
Ожидаемый результат
xml-stylesheet
type="text/xsl" href="report.xsl"

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createComment.

DOMDocument::createDocumentFragmentПодготовить несколько узлов вместе, затем вставить их одним действием в документ.

PHP 5+ · dom

DOMDocument::createDocumentFragment(): DOMDocumentFragment

Подготовить несколько узлов вместе, затем вставить их одним действием в документ.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; пустой DocumentFragment, связанный с документом. У него нет тега-обёртки.
ВставкаПри appendChild(fragment) перемещаются его дети, сам fragment не появляется в XML и после вставки становится пустым.
ВыборЭто удобство организации дерева, не обещание ускорения всей программы. Для текста нужен Text; для большого последовательного экспорта подходит XMLWriter.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$fragment = $document->createDocumentFragment();
$fragment->appendChild($document->createElement("first"));
$fragment->appendChild($document->createElement("second"));
$document->documentElement->appendChild($fragment);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><first/><second/></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$fragment = $document->createDocumentFragment();
$fragment->appendChild($document->createTextNode("Tea"));
$document->documentElement->appendChild($fragment);
echo $fragment->childNodes->length, ":", $document->documentElement->textContent, "
";
Ожидаемый результат
0:Tea

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: DOMDocument::createElement.

DOMDocument::importNodeСкопировать товар из ответа поставщика в отдельный экспорт, сохранив оригинал.

PHP 5+ · dom

DOMDocument::importNode(DOMNode $node, bool $deep = false): DOMNode|false

Скопировать товар из ответа поставщика в отдельный экспорт, сохранив оригинал.

Аргумент / поведениеПолный контракт
$nodeУзел того же семейства DOM; Document нельзя переносить таким способом, неподдержанный вид → NOT_SUPPORTED_ERR.
deepУ adoptNode нет deep: переносится тот же узел с потомками. У importNode false копирует только сам узел (правила атрибутов зависят от старого/нового API); true копирует всё поддерево. Для полного товара используйте true.
ВозвратУзел с ownerDocument целевого документа; legacy может false. При adopt это тот же объект, отсоединённый от старого родителя; при import — новый объект.
ВставкаОперация не добавляет узел в target автоматически. После неё appendChild. Modern importNode имеет nullable-параметр в API, но null не является осмысленным узлом для импорта.
Ценаimport(deep:true) выделяет копию всего поддерева. adopt не сохраняет старую ветку; выбирайте по требованию, не только по скорости.
Копирование
  1. source → product Tea
  2. target.importNode(product, true)
  3. ownerDocument=target; затем appendChild к корню
Скопировать товар и увидеть оба документаСамостоятельный пример
<?php
declare(strict_types=1);

$source = new DOMDocument();
$source->loadXML('<source><product stock="4"><title>Tea</title></product></source>');
$document = new DOMDocument();
$document->loadXML('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->importNode($product, true);
$document->documentElement->appendChild($moved);
echo 'Источник: ', $source->saveXml($source->documentElement), "\n";
echo 'Выгрузка: ', $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
Источник: <source><product stock="4"><title>Tea</title></product></source>
Выгрузка: <target><product stock="4"><title>Tea</title></product></target>

import(deep: true) копирует поддерево. Источник сохранил название и остаток, а целевой документ получил независимую копию.

Проверить идентичность и документ-владелецСамостоятельный пример
<?php
declare(strict_types=1);

$source = new DOMDocument();
$source->loadXML('<source><product stock="4"><title>Tea</title></product></source>');
$document = new DOMDocument();
$document->loadXML('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->importNode($product, true);
var_dump($moved === $product);
var_dump($moved->ownerDocument === $document);
Ожидаемый результат
bool(false)
bool(true)

Равенство содержимого и идентичность объекта — разные вопросы.

Связанные операции: DOMDocument::adoptNode.

DOMDocument::adoptNodeПеренести товар в другой документ без копии.

PHP 8.3+ · dom

DOMDocument::adoptNode(DOMNode $node): DOMNode|false

Перенести товар в другой документ без копии.

Аргумент / поведениеПолный контракт
$nodeУзел того же семейства DOM; Document нельзя переносить таким способом, неподдержанный вид → NOT_SUPPORTED_ERR.
deepУ adoptNode нет deep: переносится тот же узел с потомками. У importNode false копирует только сам узел (правила атрибутов зависят от старого/нового API); true копирует всё поддерево. Для полного товара используйте true.
ВозвратУзел с ownerDocument целевого документа; legacy может false. При adopt это тот же объект, отсоединённый от старого родителя; при import — новый объект.
ВставкаОперация не добавляет узел в target автоматически. После неё appendChild. Modern importNode имеет nullable-параметр в API, но null не является осмысленным узлом для импорта.
Ценаimport(deep:true) выделяет копию всего поддерева. adopt не сохраняет старую ветку; выбирайте по требованию, не только по скорости.
Перенос
  1. source → product Tea
  2. target.adoptNode(product)
  3. ownerDocument=target; затем appendChild к корню
Перенести товар и увидеть оба документаСамостоятельный пример
<?php
declare(strict_types=1);

$source = new DOMDocument();
$source->loadXML('<source><product stock="4"><title>Tea</title></product></source>');
$document = new DOMDocument();
$document->loadXML('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->adoptNode($product);
$document->documentElement->appendChild($moved);
echo 'Источник: ', $source->saveXml($source->documentElement), "\n";
echo 'Выгрузка: ', $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
Источник: <source/>
Выгрузка: <target><product stock="4"><title>Tea</title></product></target>

adopt перемещает тот же узел, поэтому источник опустел.

Проверить идентичность и документ-владелецСамостоятельный пример
<?php
declare(strict_types=1);

$source = new DOMDocument();
$source->loadXML('<source><product stock="4"><title>Tea</title></product></source>');
$document = new DOMDocument();
$document->loadXML('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->adoptNode($product);
var_dump($moved === $product);
var_dump($moved->ownerDocument === $document);
Ожидаемый результат
bool(true)
bool(true)

Равенство содержимого и идентичность объекта — разные вопросы.

Связанные операции: DOMDocument::importNode.

DOMDocument::registerNodeClassДобавить предметный метод узлам каталога, чтобы в прикладном коде работать с понятным действием, а не повторять доступ к полям.

PHP 5+ · dom

DOMDocument::registerNodeClass(string $baseClass, ?string $extendedClass): true

Добавить предметный метод узлам каталога, чтобы в прикладном коде работать с понятным действием, а не повторять доступ к полям.

Аргумент / поведениеПолный контракт
$baseClass / $extendedClassИмя поддерживаемого DOM-класса и его наследника того же семейства; null снимает регистрацию. Неверная пара/непроизводный класс отклоняется TypeError/ValueError.
Возвратvoid в modern; true в legacy (tentative true с PHP 8.4). Регистрация относится к конкретному документу.
КонструкторPHP-конструктор зарегистрированного класса не вызывается DOM. Нельзя требовать, чтобы он заполнил обязательные свойства.
Жизненный циклPHP-обёртки узлов временные: после потери всех PHP-ссылок могут создаваться заново. Дополнительные свойства могут теряться; храните устойчивые данные в самом дереве или отдельной модели.
ПрименимостьПолезно для методов чтения/преобразования узла. Для сложной бизнес-логики часто понятнее отдельный сервис, принимающий DOM-узел.
Настройка фабрики одного документа
  1. CatalogElement extends DOMElement
  2. registerNodeClass связывает базу и наследника
  3. createElement("product") возвращает объект с методом label()
Получить понятную строку остатка из узла товараСамостоятельный пример
<?php
declare(strict_types=1);

class StockElement extends DOMElement {
    public function stockLine(): string {
        return $this->textContent . ': ' . $this->getAttribute('stock') . ' шт.';
    }
}
$document = new DOMDocument();
$document->loadXML('<catalog/>');
$document->registerNodeClass(DOMElement::class, StockElement::class);
$product = $document->createElement('product');
$product->textContent = 'Tea';
$product->setAttribute('stock', '4');
echo $product->stockLine(), "\n";
Ожидаемый результат
Tea: 4 шт.

Вместо повторения textContent/getAttribute прикладной код вызывает stockLine. Метод только читает поля текущего узла; конструктор наследника DOM не вызывает. В этом локальном примере известный stock задан самим кодом; входящий остаток нужно отдельно валидировать.

Снять регистрацию для последующих объектовСамостоятельный пример
<?php
declare(strict_types=1);

class CatalogElement extends DOMElement {
    public function label(): string {
        return "field:" . $this->localName;
    }
}
$document = new DOMDocument();
$document->loadXML('<catalog/>');
$document->registerNodeClass(DOMElement::class, CatalogElement::class);
$document->registerNodeClass(DOMElement::class, null);
echo get_class($document->createElement("product")), "
";
Ожидаемый результат
DOMElement

Изменяется дальнейшая фабрика этого документа. Уже созданные PHP-объекты автоматически не меняют класс.

Связанные операции: DOMDocument::createElement.

Dom\Document::createElementСоздать поле/элемент XML-экспорта, не собирая открывающие и закрывающие теги вручную.

PHP 8.4+ · dom

Dom\Document::createElement(string $localName): Dom\Element

Создать поле/элемент XML-экспорта, не собирая открывающие и закрывающие теги вручную.

Аргумент / поведениеПолный контракт
localNameИмя тега; нельзя использовать недопустимые XML-символы. Modern HTMLDocument создаёт HTML-элемент в HTML namespace; XMLDocument создаёт обычный XML-элемент.
valueВ modern такого аргумента нет: создайте Text или присвойте textContent. В legacy необязательный value НЕ полностью экранирует &: для произвольного текста тоже используйте createTextNode.
Возврат / ошибкиElement; legacy также false при нестрогой ошибке. INVALID_CHARACTER_ERR для имени. Создание не вставляет узел в документ.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createElement("product");
$document->documentElement->appendChild($node);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><product/></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createElement("title");
$node->appendChild($document->createTextNode("Tea & coffee"));
echo $document->saveXml($node), "
";
Ожидаемый результат
<title>Tea &amp; coffee</title>

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createTextNode.

Dom\Document::createElementNSСоздать элемент конкретного XML-контракта с правильным пространством имён.

PHP 8.4+ · dom

Dom\Document::createElementNS(?string $namespace, string $qualifiedName): Dom\Element

Создать элемент конкретного XML-контракта с правильным пространством имён.

Аргумент / поведениеПолный контракт
namespace / qualifiedNameURI/null и имя вида p:product. Префикс требует URI; xml/xmlns имеют зарезервированные значения.
valueModern принимает только namespace и qualifiedName; legacy имеет дополнительный value с теми же ограничениями экранирования, что createElement.
Возврат / ошибкиElement (legacy может false); INVALID_CHARACTER_ERR/NAMESPACE_ERR при неверном имени/связи префикса и URI.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createElementNS("urn:price", "p:amount");
echo $node->namespaceURI, ":", $node->localName, "
";
Ожидаемый результат
urn:price:amount

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createElementNS("urn:price", "p:amount");
$node->appendChild($document->createTextNode("1200"));
$document->documentElement->appendChild($node);
echo $document->getElementsByTagNameNS("urn:price", "amount")->item(0)->textContent, "
";
Ожидаемый результат
1200

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createElement.

Dom\Document::createAttributeПодготовить объект поля XML перед присоединением к элементу.

PHP 8.4+ · dom

Dom\Document::createAttribute(string $localName): Dom\Attr

Подготовить объект поля XML перед присоединением к элементу.

Аргумент / поведениеПолный контракт
localNameИмя нового атрибута. Недопустимое имя → INVALID_CHARACTER_ERR.
ВозвратНовый Attr с пустым значением и ownerElement=null; legacy допускает false при нестрогой ошибке.
ДальшеПрисвойте value и передайте setAttributeNode. Для простого изменения одной строки короче Element::setAttribute.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$attribute = $document->createAttribute("stock");
$attribute->value = "4";
$document->documentElement->setAttributeNode($attribute);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog stock="4"/>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$attribute = $document->createAttribute("currency");
$attribute->value = "RUB";
var_dump($attribute->ownerElement);
echo $attribute->value, "
";
Ожидаемый результат
NULL
RUB

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createAttributeNS.

Dom\Document::createAttributeNSСоздать атрибут внешней схемы, сохранив URI отдельно от его короткого префикса.

PHP 8.4+ · dom

Dom\Document::createAttributeNS(?string $namespace, string $qualifiedName): Dom\Attr

Создать атрибут внешней схемы, сохранив URI отдельно от его короткого префикса.

Аргумент / поведениеПолный контракт
namespace / qualifiedNameURI/null и имя с префиксом. NAMESPACE_ERR при противоречивой комбинации; INVALID_CHARACTER_ERR при неверном имени.
ВозвратСвободный Attr; legacy может false. В старом API для корректной привязки namespace документ должен иметь корневой элемент; наши примеры его создают заранее.
ЗначениеЗадаётся через Attr.value и прикрепляется setAttributeNodeNS. xmlns не является произвольной пользовательской областью.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$attribute = $document->createAttributeNS("urn:price", "p:amount");
$attribute->value = "1200";
$document->documentElement->setAttributeNodeNS($attribute);
echo $document->documentElement->getAttributeNS("urn:price", "amount"), "
";
Ожидаемый результат
1200

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$attribute = $document->createAttributeNS("urn:delivery", "d:days");
echo $attribute->prefix, ":", $attribute->localName, ":", $attribute->namespaceURI, "
";
Ожидаемый результат
d:days:urn:delivery

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createAttribute.

Dom\Document::createTextNodeПоместить название товара или пользовательский текст в XML без превращения специальных символов в разметку.

PHP 8.4+ · dom

Dom\Document::createTextNode(string $data): Dom\Text

Поместить название товара или пользовательский текст в XML без превращения специальных символов в разметку.

Аргумент / поведениеПолный контракт
dataСтрока текста в UTF-8. Передаётся как данные, а не XML-фрагмент. Не делайте htmlspecialchars заранее.
ВозвратText с ownerDocument текущего документа. Сериализатор экранирует &, < по правилам формата.</td>
ГраницаКорректное XML-экранирование не делает произвольный URL безопасным и не разрешает данные в JavaScript/CSS-контексте. Для атрибутов используйте их отдельные операции.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$text = $document->createTextNode("Tea & coffee");
$document->documentElement->appendChild($text);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>Tea &amp; coffee</catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$text = $document->createTextNode("<strong>Sale</strong>");
echo $text->data, "
";
echo $document->saveXml($text), "
";
Ожидаемый результат
<strong>Sale</strong>
&lt;strong&gt;Sale&lt;/strong&gt;

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createElement.

Dom\Document::createCDATASectionПредставить текст как CDATA, когда получатель XML ожидает такую сериализацию описания.

PHP 8.4+ · dom

Dom\Document::createCDATASection(string $data): Dom\CDATASection

Представить текст как CDATA, когда получатель XML ожидает такую сериализацию описания.

Аргумент / поведениеПолный контракт
dataСтрока текста; последовательность ]]> завершает CDATA и недопустима внутри одной modern секции (INVALID_CHARACTER_ERR).
ВозвратCDATASection. Modern HTMLDocument запрещает CDATA (NOT_SUPPORTED_ERR); эта операция для XMLDocument.
СмыслCDATA не шифрует и не санитизирует HTML. После XML-разбора это снова строка; при вставке её в браузер нужна отдельная политика HTML.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createCDATASection("<b>Sale</b>");
$document->documentElement->appendChild($node);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><![CDATA[<b>Sale</b>]]></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$node = $document->createCDATASection("Tea & coffee");
echo $node->data, "
";
echo $node->length, "
";
Ожидаемый результат
Tea & coffee
12

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createTextNode.

Dom\Document::createCommentДобавить пояснение в генерируемый XML-файл для человека, который будет его просматривать.

PHP 8.4+ · dom

Dom\Document::createComment(string $data): Dom\Comment

Добавить пояснение в генерируемый XML-файл для человека, который будет его просматривать.

Аргумент / поведениеПолный контракт
dataТекст комментария. Не помещайте секреты: комментарии тоже выходят в сериализованный файл. Для XML избегайте -- и завершающего -, даже если создание узла это допускает.
ВозвратComment, ещё не вставленный в документ.
ГраницаКомментарий не является обычным текстом элемента и не задаёт настройку парсера. За валидность финальной XML-разметки отвечает весь процесс генерации.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$comment = $document->createComment("Generated catalog");
$document->documentElement->appendChild($comment);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><!--Generated catalog--></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$comment = $document->createComment("Updated daily");
echo $comment->data, "
";
var_dump($comment->parentNode);
Ожидаемый результат
Updated daily
NULL

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createTextNode.

Dom\Document::createProcessingInstructionДобавить инструкцию для специализированного XML-получателя, например связь с локальной таблицей стилей.

PHP 8.4+ · dom

Dom\Document::createProcessingInstruction(string $target, string $data): Dom\ProcessingInstruction

Добавить инструкцию для специализированного XML-получателя, например связь с локальной таблицей стилей.

Аргумент / поведениеПолный контракт
target / dataXML-имя назначения и строка данных. Legacy data необязательна (пустая по умолчанию), modern требует её явно. target xml зарезервирован, data не должна содержать ?>.
Возврат / ошибкиProcessingInstruction; некорректное имя/данные → INVALID_CHARACTER_ERR (legacy в отдельных случаях warning/false).
ГраницаСоздание xml-stylesheet не запускает XSLT в PHP. Обработку выполняет отдельный потребитель. Не принимайте произвольные пути/URL инструкции от клиента.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$pi = $document->createProcessingInstruction("report", "version=1");
$document->insertBefore($pi, $document->documentElement);
echo $document->saveXml($pi), "
";
Ожидаемый результат
<?report version=1?>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$pi = $document->createProcessingInstruction("xml-stylesheet", 'type="text/xsl" href="report.xsl"');
echo $pi->target, "
";
echo $pi->data, "
";
Ожидаемый результат
xml-stylesheet
type="text/xsl" href="report.xsl"

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createComment.

Dom\Document::createDocumentFragmentПодготовить несколько узлов вместе, затем вставить их одним действием в документ.

PHP 8.4+ · dom

Dom\Document::createDocumentFragment(): Dom\DocumentFragment

Подготовить несколько узлов вместе, затем вставить их одним действием в документ.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; пустой DocumentFragment, связанный с документом. У него нет тега-обёртки.
ВставкаПри appendChild(fragment) перемещаются его дети, сам fragment не появляется в XML и после вставки становится пустым.
ВыборЭто удобство организации дерева, не обещание ускорения всей программы. Для текста нужен Text; для большого последовательного экспорта подходит XMLWriter.
Документ — владелец узла
  1. document создаёт узел с ownerDocument=document
  2. Новый объект ещё не ребёнок catalog
  3. appendChild присоединяет его → saveXml сериализует дерево
Создать узел и увидеть результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$fragment = $document->createDocumentFragment();
$fragment->appendChild($document->createElement("first"));
$fragment->appendChild($document->createElement("second"));
$document->documentElement->appendChild($fragment);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><first/><second/></catalog>

Фабрика связывает узел с документом, а вставка связывает с родителем.

Разобрать поведение созданного объектаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$fragment = $document->createDocumentFragment();
$fragment->appendChild($document->createTextNode("Tea"));
$document->documentElement->appendChild($fragment);
echo $fragment->childNodes->length, ":", $document->documentElement->textContent, "
";
Ожидаемый результат
0:Tea

Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.

Связанные операции: Dom\Document::createElement.

Dom\Document::importNodeСкопировать товар из ответа поставщика в отдельный экспорт, сохранив оригинал.

PHP 8.4+ · dom

Dom\Document::importNode(?Dom\Node $node, bool $deep = false): Dom\Node

Скопировать товар из ответа поставщика в отдельный экспорт, сохранив оригинал.

Аргумент / поведениеПолный контракт
$nodeУзел того же семейства DOM; Document нельзя переносить таким способом, неподдержанный вид → NOT_SUPPORTED_ERR.
deepУ adoptNode нет deep: переносится тот же узел с потомками. У importNode false копирует только сам узел (правила атрибутов зависят от старого/нового API); true копирует всё поддерево. Для полного товара используйте true.
ВозвратУзел с ownerDocument целевого документа; legacy может false. При adopt это тот же объект, отсоединённый от старого родителя; при import — новый объект.
ВставкаОперация не добавляет узел в target автоматически. После неё appendChild. Modern importNode имеет nullable-параметр в API, но null не является осмысленным узлом для импорта.
Ценаimport(deep:true) выделяет копию всего поддерева. adopt не сохраняет старую ветку; выбирайте по требованию, не только по скорости.
Копирование
  1. source → product Tea
  2. target.importNode(product, true)
  3. ownerDocument=target; затем appendChild к корню
Скопировать товар и увидеть оба документаСамостоятельный пример
<?php
declare(strict_types=1);

$source = Dom\XMLDocument::createFromString('<source><product stock="4"><title>Tea</title></product></source>');
$document = Dom\XMLDocument::createFromString('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->importNode($product, true);
$document->documentElement->appendChild($moved);
echo 'Источник: ', $source->saveXml($source->documentElement), "\n";
echo 'Выгрузка: ', $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
Источник: <source><product stock="4"><title>Tea</title></product></source>
Выгрузка: <target><product stock="4"><title>Tea</title></product></target>

import(deep: true) копирует поддерево. Источник сохранил название и остаток, а целевой документ получил независимую копию.

Проверить идентичность и документ-владелецСамостоятельный пример
<?php
declare(strict_types=1);

$source = Dom\XMLDocument::createFromString('<source><product stock="4"><title>Tea</title></product></source>');
$document = Dom\XMLDocument::createFromString('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->importNode($product, true);
var_dump($moved === $product);
var_dump($moved->ownerDocument === $document);
Ожидаемый результат
bool(false)
bool(true)

Равенство содержимого и идентичность объекта — разные вопросы.

Связанные операции: Dom\Document::adoptNode.

Dom\Document::adoptNodeПеренести товар в другой документ без копии.

PHP 8.4+ · dom

Dom\Document::adoptNode(Dom\Node $node): Dom\Node

Перенести товар в другой документ без копии.

Аргумент / поведениеПолный контракт
$nodeУзел того же семейства DOM; Document нельзя переносить таким способом, неподдержанный вид → NOT_SUPPORTED_ERR.
deepУ adoptNode нет deep: переносится тот же узел с потомками. У importNode false копирует только сам узел (правила атрибутов зависят от старого/нового API); true копирует всё поддерево. Для полного товара используйте true.
ВозвратУзел с ownerDocument целевого документа; legacy может false. При adopt это тот же объект, отсоединённый от старого родителя; при import — новый объект.
ВставкаОперация не добавляет узел в target автоматически. После неё appendChild. Modern importNode имеет nullable-параметр в API, но null не является осмысленным узлом для импорта.
Ценаimport(deep:true) выделяет копию всего поддерева. adopt не сохраняет старую ветку; выбирайте по требованию, не только по скорости.
Перенос
  1. source → product Tea
  2. target.adoptNode(product)
  3. ownerDocument=target; затем appendChild к корню
Перенести товар и увидеть оба документаСамостоятельный пример
<?php
declare(strict_types=1);

$source = Dom\XMLDocument::createFromString('<source><product stock="4"><title>Tea</title></product></source>');
$document = Dom\XMLDocument::createFromString('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->adoptNode($product);
$document->documentElement->appendChild($moved);
echo 'Источник: ', $source->saveXml($source->documentElement), "\n";
echo 'Выгрузка: ', $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
Источник: <source/>
Выгрузка: <target><product stock="4"><title>Tea</title></product></target>

adopt перемещает тот же узел, поэтому источник опустел.

Проверить идентичность и документ-владелецСамостоятельный пример
<?php
declare(strict_types=1);

$source = Dom\XMLDocument::createFromString('<source><product stock="4"><title>Tea</title></product></source>');
$document = Dom\XMLDocument::createFromString('<target/>');
$product = $source->documentElement->firstChild;
$moved = $document->adoptNode($product);
var_dump($moved === $product);
var_dump($moved->ownerDocument === $document);
Ожидаемый результат
bool(true)
bool(true)

Равенство содержимого и идентичность объекта — разные вопросы.

Связанные операции: Dom\Document::importNode.

Dom\Document::registerNodeClassДобавить предметный метод узлам каталога, чтобы в прикладном коде работать с понятным действием, а не повторять доступ к полям.

PHP 8.4+ · dom

Dom\Document::registerNodeClass(string $baseClass, ?string $extendedClass): void

Добавить предметный метод узлам каталога, чтобы в прикладном коде работать с понятным действием, а не повторять доступ к полям.

Аргумент / поведениеПолный контракт
$baseClass / $extendedClassИмя поддерживаемого DOM-класса и его наследника того же семейства; null снимает регистрацию. Неверная пара/непроизводный класс отклоняется TypeError/ValueError.
Возвратvoid в modern; true в legacy (tentative true с PHP 8.4). Регистрация относится к конкретному документу.
КонструкторPHP-конструктор зарегистрированного класса не вызывается DOM. Нельзя требовать, чтобы он заполнил обязательные свойства.
Жизненный циклPHP-обёртки узлов временные: после потери всех PHP-ссылок могут создаваться заново. Дополнительные свойства могут теряться; храните устойчивые данные в самом дереве или отдельной модели.
ПрименимостьПолезно для методов чтения/преобразования узла. Для сложной бизнес-логики часто понятнее отдельный сервис, принимающий DOM-узел.
Настройка фабрики одного документа
  1. CatalogElement extends Dom\Element
  2. registerNodeClass связывает базу и наследника
  3. createElement("product") возвращает объект с методом label()
Получить понятную строку остатка из узла товараСамостоятельный пример
<?php
declare(strict_types=1);

class StockElement extends Dom\Element {
    public function stockLine(): string {
        return $this->textContent . ': ' . $this->getAttribute('stock') . ' шт.';
    }
}
$document = Dom\XMLDocument::createFromString('<catalog/>');
$document->registerNodeClass(Dom\Element::class, StockElement::class);
$product = $document->createElement('product');
$product->textContent = 'Tea';
$product->setAttribute('stock', '4');
echo $product->stockLine(), "\n";
Ожидаемый результат
Tea: 4 шт.

Вместо повторения textContent/getAttribute прикладной код вызывает stockLine. Метод только читает поля текущего узла; конструктор наследника DOM не вызывает. В этом локальном примере известный stock задан самим кодом; входящий остаток нужно отдельно валидировать.

Снять регистрацию для последующих объектовСамостоятельный пример
<?php
declare(strict_types=1);

class CatalogElement extends Dom\Element {
    public function label(): string {
        return "field:" . $this->localName;
    }
}
$document = Dom\XMLDocument::createFromString('<catalog/>');
$document->registerNodeClass(Dom\Element::class, CatalogElement::class);
$document->registerNodeClass(Dom\Element::class, null);
echo get_class($document->createElement("product")), "
";
Ожидаемый результат
Dom\Element

Изменяется дальнейшая фабрика этого документа. Уже созданные PHP-объекты автоматически не меняют класс.

Связанные операции: Dom\Document::createElement.

Dom\Document::importLegacyNodeПеренести результат старой библиотеки DOMDocument в новый API Dom, не смешивая несовместимые объекты напрямую.

PHP 8.4+ · dom

Dom\Document::importLegacyNode(DOMNode $node, bool $deep = false): Dom\Node

Перенести результат старой библиотеки DOMDocument в новый API Dom, не смешивая несовместимые объекты напрямую.

Аргумент / поведениеПолный контракт
$node / $deep=falseСтарый DOMNode и глубина копирования. true сохраняет потомков. Это мост между семействами, а не обычный importNode modern → modern.
ВозвратНовый современный Node в целевом документе; оригинал старого семейства сохраняется.
Ошибки / различияНеподдержанные узлы отклоняются DOMException. Не все исторические особенности старого дерева соответствуют modern DOM: namespace-узлы и HTML namespace требуют осмысленной миграции.
ЦенаКопия поддерева выделяет память. Для нового проекта выбирайте одно семейство API с начала; мост нужен для совместимости.
Мост API
  1. Старая библиотека → DOMElement product
  2. modernDocument.importLegacyNode(old, true)
  3. Dom\Element product → вставка в новый документ
Скопировать товар старого DOM в современныйСамостоятельный пример
<?php
declare(strict_types=1);

$old = new DOMDocument();
$old->loadXML('<product>Tea</product>');
$document = Dom\XMLDocument::createFromString('<catalog/>');
$copy = $document->importLegacyNode($old->documentElement, true);
$document->documentElement->appendChild($copy);
echo get_class($copy), ":", $copy->textContent, "
";
Ожидаемый результат
Dom\Element:Tea

Оба документа продолжают существовать; скопирован только выбранный узел.

Изменение копии не меняет старое деревоСамостоятельный пример
<?php
declare(strict_types=1);

$old = new DOMDocument();
$old->loadXML('<product>Tea</product>');
$document = Dom\XMLDocument::createFromString('<catalog/>');
$copy = $document->importLegacyNode($old->documentElement, true);
$copy->textContent = "Coffee";
echo $old->documentElement->textContent, ":", $copy->textContent, "
";
Ожидаемый результат
Tea:Coffee

Этот мост создаёт копию, в отличие от shared-view мостов SimpleXML и DOM.

Связанные операции: Dom\Document::importNode, dom_import_simplexml.

DOMDocument::loadXMLЗагрузить XML-каталог из уже полученной строки.

PHP 5+ · dom

DOMDocument::loadXML(string $source, int $options = 0): bool

Загрузить XML-каталог из уже полученной строки.

Аргумент / поведениеПолный контракт
ИсточникСтрока с содержимым. Путь catalog.xml не читается как файл — для этого load/loadHTMLFile.
$options = 0Битовая маска LIBXML_*. Не включайте DTDLOAD/NOENT/PARSEHUGE ради «чтобы заработало»: они меняют доступ к внешним ресурсам и ограничения парсера. NONET блокирует сеть, но не является запретом локальных файлов.
Возврат / эффектbool; при успехе заполняется текущий DOMDocument. Ошибки libxml доступны через libxml_use_internal_errors/get_errors. Ошибки аргументов (например пустой источник в актуальном PHP) могут быть ValueError вне libxml.
СовместимостьМетод нестатический; вызов DOMDocument::loadXML без объекта недопустим с PHP 8.0.
Правила разбораXML должен иметь один корень, корректную вложенность и кодировку. Успешный разбор ещё не означает соответствие XSD/бизнес-контракту.
Источник → дерево
  1. Строка уже находится в PHP
  2. load* разбирает разметку и заполняет document
  3. Ищем узлы; получаем Tea; исходный файл сам не меняется
Разобрать полученную строку и прочитать названиеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->loadXML('<catalog><product>Tea</product></catalog>');
if (!$ok) { throw new RuntimeException("Не удалось прочитать каталог"); }
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Загрузка заполняет дерево, затем чтение выбранного узла даёт конкретное название. Файл входит в архив; строковый вариант не принимает имя файла вместо его содержимого.

Отделить изменение дерева от исходного документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->loadXML('<catalog><product>Tea</product></catalog>');
$node = $document->getElementsByTagName("product")->item(0);
$node->textContent = "Coffee";
echo $node->textContent, "
";
Ожидаемый результат
Coffee

Изменение существует в памяти. Для записи результата отдельно вызывается save/saveHTMLFile.

Связанные операции: DOMDocument::saveXML, Dom\XMLDocument::createFromString.

DOMDocument::loadЗагрузить XML-каталог из контролируемого локального файла.

PHP 5+ · dom

DOMDocument::load(string $filename, int $options = 0): bool

Загрузить XML-каталог из контролируемого локального файла.

Аргумент / поведениеПолный контракт
ИсточникПуть/поддерживаемый stream wrapper. Передавайте путь своего приложения, не произвольный URL клиента.
$options = 0Битовая маска LIBXML_*. Не включайте DTDLOAD/NOENT/PARSEHUGE ради «чтобы заработало»: они меняют доступ к внешним ресурсам и ограничения парсера. NONET блокирует сеть, но не является запретом локальных файлов.
Возврат / эффектbool; при успехе заполняется текущий DOMDocument. Ошибки libxml доступны через libxml_use_internal_errors/get_errors. Ошибки аргументов (например пустой источник в актуальном PHP) могут быть ValueError вне libxml.
СовместимостьМетод нестатический; вызов DOMDocument::load без объекта недопустим с PHP 8.0.
Правила разбораXML должен иметь один корень, корректную вложенность и кодировку. Успешный разбор ещё не означает соответствие XSD/бизнес-контракту.
Источник → дерево
  1. Файл читается
  2. load* разбирает разметку и заполняет document
  3. Ищем узлы; получаем Tea; исходный файл сам не меняется
Прочитать название из catalog.xmlСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->load("catalog.xml");
if (!$ok) { throw new RuntimeException("Не удалось прочитать каталог"); }
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Загрузка заполняет дерево, затем чтение выбранного узла даёт конкретное название. Файл входит в архив; строковый вариант не принимает имя файла вместо его содержимого.

Отделить изменение дерева от исходного документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->load("catalog.xml");
$node = $document->getElementsByTagName("product")->item(0);
$node->textContent = "Coffee";
echo $node->textContent, "
";
Ожидаемый результат
Coffee

Изменение существует в памяти. Для записи результата отдельно вызывается save/saveHTMLFile.

Связанные операции: DOMDocument::saveXML, Dom\XMLDocument::createFromFile.

DOMDocument::loadHTMLЗагрузить HTML-страницу старым API из уже полученной строки.

PHP 5+ · dom

DOMDocument::loadHTML(string $source, int $options = 0): bool

Загрузить HTML-страницу старым API из уже полученной строки.

Аргумент / поведениеПолный контракт
ИсточникСтрока с содержимым. Путь catalog.xml не читается как файл — для этого load/loadHTMLFile.
$options = 0Битовая маска LIBXML_*. Не включайте DTDLOAD/NOENT/PARSEHUGE ради «чтобы заработало»: они меняют доступ к внешним ресурсам и ограничения парсера. NONET блокирует сеть, но не является запретом локальных файлов.
Возврат / эффектbool; при успехе заполняется текущий DOMDocument. Ошибки libxml доступны через libxml_use_internal_errors/get_errors. Ошибки аргументов (например пустой источник в актуальном PHP) могут быть ValueError вне libxml.
СовместимостьМетод нестатический; вызов DOMDocument::loadHTML без объекта недопустим с PHP 8.0.
Правила разбораЭто HTML4/libxml parser, результат может отличаться от браузера HTML5. Для современного HTML используйте Dom\HTMLDocument; старый parser нельзя считать безопасным HTML-санитайзером.
Источник → дерево
  1. Строка уже находится в PHP
  2. load* разбирает разметку и заполняет document
  3. Ищем узлы; получаем Tea; исходный файл сам не меняется
Разобрать полученную строку и прочитать названиеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->loadHTML('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
if (!$ok) { throw new RuntimeException("Не удалось прочитать каталог"); }
echo $document->getElementsByTagName("p")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Загрузка заполняет дерево, затем чтение выбранного узла даёт конкретное название. Файл входит в архив; строковый вариант не принимает имя файла вместо его содержимого.

Отделить изменение дерева от исходного документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->loadHTML('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$node = $document->getElementsByTagName("p")->item(0);
$node->textContent = "Coffee";
echo $node->textContent, "
";
Ожидаемый результат
Coffee

Изменение существует в памяти. Для записи результата отдельно вызывается save/saveHTMLFile.

Связанные операции: DOMDocument::saveHTML, Dom\HTMLDocument::createFromString.

DOMDocument::loadHTMLFileЗагрузить HTML-страницу старым API из контролируемого локального файла.

PHP 5+ · dom

DOMDocument::loadHTMLFile(string $filename, int $options = 0): bool

Загрузить HTML-страницу старым API из контролируемого локального файла.

Аргумент / поведениеПолный контракт
ИсточникПуть/поддерживаемый stream wrapper. Передавайте путь своего приложения, не произвольный URL клиента.
$options = 0Битовая маска LIBXML_*. Не включайте DTDLOAD/NOENT/PARSEHUGE ради «чтобы заработало»: они меняют доступ к внешним ресурсам и ограничения парсера. NONET блокирует сеть, но не является запретом локальных файлов.
Возврат / эффектbool; при успехе заполняется текущий DOMDocument. Ошибки libxml доступны через libxml_use_internal_errors/get_errors. Ошибки аргументов (например пустой источник в актуальном PHP) могут быть ValueError вне libxml.
СовместимостьМетод нестатический; вызов DOMDocument::loadHTMLFile без объекта недопустим с PHP 8.0.
Правила разбораЭто HTML4/libxml parser, результат может отличаться от браузера HTML5. Для современного HTML используйте Dom\HTMLDocument; старый parser нельзя считать безопасным HTML-санитайзером.
Источник → дерево
  1. Файл читается
  2. load* разбирает разметку и заполняет document
  3. Ищем узлы; получаем Tea; исходный файл сам не меняется
Прочитать название из catalog.htmlСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->loadHTMLFile("catalog.html");
if (!$ok) { throw new RuntimeException("Не удалось прочитать каталог"); }
echo $document->getElementsByTagName("p")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Загрузка заполняет дерево, затем чтение выбранного узла даёт конкретное название. Файл входит в архив; строковый вариант не принимает имя файла вместо его содержимого.

Отделить изменение дерева от исходного документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$ok = $document->loadHTMLFile("catalog.html");
$node = $document->getElementsByTagName("p")->item(0);
$node->textContent = "Coffee";
echo $node->textContent, "
";
Ожидаемый результат
Coffee

Изменение существует в памяти. Для записи результата отдельно вызывается save/saveHTMLFile.

Связанные операции: DOMDocument::saveHTML, Dom\HTMLDocument::createFromFile.

Dom\XMLDocument::createFromStringПолучить современное дерево XML из строки.

PHP 8.4+ · dom

static Dom\XMLDocument::createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): Dom\XMLDocument

Получить современное дерево XML из строки.

Аргумент / поведениеПолный контракт
Первый аргументСодержимое документа, не имя файла. XML/HTML уже получен, например, от HTTP-клиента.
$options=0XML-флаги libxml; не разрешайте внешние сущности/DTD и не отключайте лимиты для недоверенного документа. Неподдержанные флаги → ValueError.
$overrideEncoding=nullПринудительная кодировка входных байтов; null оставляет определение парсеру. Неизвестное имя → ValueError. Это кодировка ВХОДА, не имя кодировки будущего HTTP-ответа.
ВозвратНовый final Dom\XMLDocument; текущий объект не требуется, это статическая фабрика через ::.
ОшибкиНевозможность прочитать/разобрать документ → Exception/диагностика libxml; неправильные аргументы → ValueError. Некорректный HTML часто восстанавливается по HTML-правилам, XML значительно строже.
СтоимостьDOM хранит полное дерево в памяти. Для большого XML с последовательной обработкой выбирайте XMLReader. HTML parser не выполняет JS, CSS, загрузку картинок.
Фабрика документа
  1. Входные байты XML
  2. Статическая createFromString создаёт дерево
  3. Переменная document получает готовый объект, затем читаем узлы
Прочитать товар из нового документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.

Прочитать русский текст в согласованной UTF-8-кодировкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Чай — 4 шт.</product></catalog>', overrideEncoding: "UTF-8");
echo $document->getElementsByTagName("product")->item(0)->textContent, "\n";
Ожидаемый результат
Чай — 4 шт.

overrideEncoding относится к входным байтам. В учебном файле/строке они действительно UTF-8, поэтому русские буквы прочитаны без потери. Нельзя исправить неизвестную кодировку произвольным названием; сначала выясняют договор поставщика.

Связанные операции: Dom\XMLDocument::createEmpty, Dom\XMLDocument::saveXml.

Dom\XMLDocument::createFromFileПолучить современное дерево XML из файла.

PHP 8.4+ · dom

static Dom\XMLDocument::createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): Dom\XMLDocument

Получить современное дерево XML из файла.

Аргумент / поведениеПолный контракт
Первый аргументПуть файла/поддерживаемый поток; источник выбирает серверное приложение.
$options=0XML-флаги libxml; не разрешайте внешние сущности/DTD и не отключайте лимиты для недоверенного документа. Неподдержанные флаги → ValueError.
$overrideEncoding=nullПринудительная кодировка входных байтов; null оставляет определение парсеру. Неизвестное имя → ValueError. Это кодировка ВХОДА, не имя кодировки будущего HTTP-ответа.
ВозвратНовый final Dom\XMLDocument; текущий объект не требуется, это статическая фабрика через ::.
ОшибкиНевозможность прочитать/разобрать документ → Exception/диагностика libxml; неправильные аргументы → ValueError. Некорректный HTML часто восстанавливается по HTML-правилам, XML значительно строже.
СтоимостьDOM хранит полное дерево в памяти. Для большого XML с последовательной обработкой выбирайте XMLReader. HTML parser не выполняет JS, CSS, загрузку картинок.
Фабрика документа
  1. Входные байты XML
  2. Статическая createFromFile создаёт дерево
  3. Переменная document получает готовый объект, затем читаем узлы
Прочитать товар из нового документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromFile("catalog.xml");
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.

Прочитать русский текст в согласованной UTF-8-кодировкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromFile(__DIR__ . '/catalog.xml', overrideEncoding: "UTF-8");
echo $document->getElementsByTagName("product")->item(0)->textContent, "\n";
Ожидаемый результат
Чай — 4 шт.

overrideEncoding относится к входным байтам. В учебном файле/строке они действительно UTF-8, поэтому русские буквы прочитаны без потери. Нельзя исправить неизвестную кодировку произвольным названием; сначала выясняют договор поставщика.

Связанные операции: Dom\XMLDocument::createEmpty, Dom\XMLDocument::saveXml.

Dom\XMLDocument::createEmptyНачать генерацию нового XML документа с известной кодировкой, когда читать исходный файл не нужно.

PHP 8.4+ · dom

static Dom\XMLDocument::createEmpty(string $version = "1.0", string $encoding = "UTF-8"): Dom\XMLDocument

Начать генерацию нового XML документа с известной кодировкой, когда читать исходный файл не нужно.

Аргумент / поведениеПолный контракт
Аргументыversion="1.0" и encoding="UTF-8" для XML. Версия XML не является версией PHP.
ВозвратПустой Dom\XMLDocument без корневого элемента. Даже HTML createEmpty не создаёт автоматически html/head/body.
ОшибкиНеподдержанная кодировка/недопустимая версия → ValueError. Созданное дерево ещё нужно наполнить через createElement/appendChild.
ВыборДля HTML-скелета с head/body пригодится Implementation::createHTMLDocument; для копии существующего документа — соответствующая фабрика парсинга.
Новый документ
  1. createEmpty → documentElement=null
  2. createElement создаёт корень
  3. appendChild связывает его с document
Создать корень будущего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createEmpty();
$document->appendChild($document->createElement("catalog"));
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog/>

createEmpty создаёт владельца дерева, createElement — корень, appendChild соединяет их. Только после вставки корень попадает в сериализацию. Кодировка фабрики по умолчанию UTF-8.

Проверить владение созданным элементомСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createEmpty("1.0", "UTF-8");
$element = $document->createElement("product");
var_dump($element->ownerDocument === $document);
var_dump($element->parentNode);
Ожидаемый результат
bool(true)
NULL

ownerDocument уже установлен, parentNode ещё нет.

Связанные операции: Dom\Document::createElement, Dom\XMLDocument::createFromString.

Dom\HTMLDocument::createFromStringПолучить современное дерево HTML по правилам браузерного стандарта из строки.

PHP 8.4+ · dom

static Dom\HTMLDocument::createFromString(string $source, int $options = 0, ?string $overrideEncoding = null): Dom\HTMLDocument

Получить современное дерево HTML по правилам браузерного стандарта из строки.

Аргумент / поведениеПолный контракт
Первый аргументСодержимое документа, не имя файла. XML/HTML уже получен, например, от HTTP-клиента.
$options=0Разрешённые для parser флаги LIBXML_*. Неподдержанная маска → ValueError. Для HTML также Dom\HTML_NO_DEFAULT_NS: отключает стандартный HTML namespace и template-семантику; не включайте его ради обхода namespace в запросе.
$overrideEncoding=nullПринудительная кодировка входных байтов; null оставляет определение парсеру. Неизвестное имя → ValueError. Это кодировка ВХОДА, не имя кодировки будущего HTTP-ответа.
ВозвратНовый final Dom\HTMLDocument; текущий объект не требуется, это статическая фабрика через ::.
ОшибкиНевозможность прочитать/разобрать документ → Exception/диагностика libxml; неправильные аргументы → ValueError. Некорректный HTML часто восстанавливается по HTML-правилам, XML значительно строже.
СтоимостьDOM хранит полное дерево в памяти. Для большого XML с последовательной обработкой выбирайте XMLReader. HTML parser не выполняет JS, CSS, загрузку картинок.
Фабрика документа
  1. Входные байты HTML
  2. Статическая createFromString создаёт дерево
  3. Переменная document получает готовый объект, затем читаем узлы
Прочитать товар из нового документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
echo $document->getElementsByTagName("p")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.

Прочитать русский текст в согласованной UTF-8-кодировкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html lang="ru"><head><meta charset="UTF-8"><title>Каталог</title></head><body><h1>Остатки</h1><p class="product">Чай — 4 шт.</p></body></html>', overrideEncoding: "UTF-8");
echo $document->title, "\n";
echo $document->getElementsByTagName("p")->item(0)->textContent, "\n";
Ожидаемый результат
Каталог
Чай — 4 шт.

overrideEncoding относится к входным байтам. В учебном файле/строке они действительно UTF-8, поэтому русские буквы прочитаны без потери. Нельзя исправить неизвестную кодировку произвольным названием; сначала выясняют договор поставщика.

Связанные операции: Dom\HTMLDocument::createEmpty, Dom\HTMLDocument::saveHtml.

Dom\HTMLDocument::createFromFileПолучить современное дерево HTML по правилам браузерного стандарта из файла.

PHP 8.4+ · dom

static Dom\HTMLDocument::createFromFile(string $path, int $options = 0, ?string $overrideEncoding = null): Dom\HTMLDocument

Получить современное дерево HTML по правилам браузерного стандарта из файла.

Аргумент / поведениеПолный контракт
Первый аргументПуть файла/поддерживаемый поток; источник выбирает серверное приложение.
$options=0Разрешённые для parser флаги LIBXML_*. Неподдержанная маска → ValueError. Для HTML также Dom\HTML_NO_DEFAULT_NS: отключает стандартный HTML namespace и template-семантику; не включайте его ради обхода namespace в запросе.
$overrideEncoding=nullПринудительная кодировка входных байтов; null оставляет определение парсеру. Неизвестное имя → ValueError. Это кодировка ВХОДА, не имя кодировки будущего HTTP-ответа.
ВозвратНовый final Dom\HTMLDocument; текущий объект не требуется, это статическая фабрика через ::.
ОшибкиНевозможность прочитать/разобрать документ → Exception/диагностика libxml; неправильные аргументы → ValueError. Некорректный HTML часто восстанавливается по HTML-правилам, XML значительно строже.
СтоимостьDOM хранит полное дерево в памяти. Для большого XML с последовательной обработкой выбирайте XMLReader. HTML parser не выполняет JS, CSS, загрузку картинок.
Фабрика документа
  1. Входные байты HTML
  2. Статическая createFromFile создаёт дерево
  3. Переменная document получает готовый объект, затем читаем узлы
Прочитать товар из нового документаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromFile("catalog.html");
echo $document->getElementsByTagName("p")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.

Прочитать русский текст в согласованной UTF-8-кодировкеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromFile(__DIR__ . '/catalog.html', overrideEncoding: "UTF-8");
echo $document->title, "\n";
echo $document->getElementsByTagName("p")->item(0)->textContent, "\n";
Ожидаемый результат
Каталог
Чай — 4 шт.

overrideEncoding относится к входным байтам. В учебном файле/строке они действительно UTF-8, поэтому русские буквы прочитаны без потери. Нельзя исправить неизвестную кодировку произвольным названием; сначала выясняют договор поставщика.

Связанные операции: Dom\HTMLDocument::createEmpty, Dom\HTMLDocument::saveHtml.

Dom\HTMLDocument::createEmptyНачать генерацию нового HTML документа с известной кодировкой, когда читать исходный файл не нужно.

PHP 8.4+ · dom

static Dom\HTMLDocument::createEmpty(string $encoding = "UTF-8"): Dom\HTMLDocument

Начать генерацию нового HTML документа с известной кодировкой, когда читать исходный файл не нужно.

Аргумент / поведениеПолный контракт
Аргументыencoding="UTF-8" для HTML.
ВозвратПустой Dom\HTMLDocument без корневого элемента. Даже HTML createEmpty не создаёт автоматически html/head/body.
ОшибкиНеподдержанная кодировка/недопустимая версия → ValueError. Созданное дерево ещё нужно наполнить через createElement/appendChild.
ВыборДля HTML-скелета с head/body пригодится Implementation::createHTMLDocument; для копии существующего документа — соответствующая фабрика парсинга.
Новый документ
  1. createEmpty → documentElement=null
  2. createElement создаёт корень
  3. appendChild связывает его с document
Создать корень будущего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$document->appendChild($document->createElement("html"));
echo $document->saveHtml($document->documentElement), "\n";
Ожидаемый результат
<html></html>

createEmpty создаёт владельца дерева, createElement — корень, appendChild соединяет их. Только после вставки корень попадает в сериализацию. Кодировка фабрики по умолчанию UTF-8.

Проверить владение созданным элементомСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty("UTF-8");
$element = $document->createElement("article");
var_dump($element->ownerDocument === $document);
var_dump($element->parentNode);
Ожидаемый результат
bool(true)
NULL

ownerDocument уже установлен, parentNode ещё нет.

Связанные операции: Dom\Document::createElement, Dom\HTMLDocument::createFromString.

DOMDocument::__constructСоздать старый DOMDocument для совместимого XML-кода, затем наполнить его узлами или загрузить документ.

PHP 5+ · dom

DOMDocument::__construct(string $version = "1.0", string $encoding = "")

Создать старый DOMDocument для совместимого XML-кода, затем наполнить его узлами или загрузить документ.

Аргумент / поведениеПолный контракт
$version="1.0" / $encoding=""Версия XML и желаемая кодировка документа. Конструктор не читает XML и не создаёт корень.
ВозвратКонструктор не возвращает значение; new возвращает созданный объект. Параметры версии/кодировки не заменяют корректное декодирование входных байтов.
Современная альтернативаDom\XMLDocument::createEmpty или createFromString с PHP 8.4. Не смешивайте классы двух семейств.
Начало генерации
  1. new DOMDocument("1.0", "UTF-8")
  2. Пустой документ-владелец
  3. Создание и вставка catalog
Создать корневой элементСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument("1.0", "UTF-8");
$document->appendChild($document->createElement("catalog"));
echo $document->saveXML($document->documentElement), "
";
Ожидаемый результат
<catalog/>

new создаёт документ, appendChild добавляет корень.

Посмотреть заданные параметрыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument("1.0", "UTF-8");
echo $document->xmlVersion, ":", $document->encoding, "
";
var_dump($document->documentElement);
Ожидаемый результат
1.0:UTF-8
NULL

Кодировка задана, но данных в дереве ещё нет.

Связанные операции: DOMDocument::loadXML, Dom\XMLDocument::createEmpty.

DOMDocument::saveXMLСохранить сформированное дерево в строку для ответа или дальнейшей обработки.

PHP 5+ · dom

DOMDocument::saveXML(?DOMNode $node = null, int $options = 0): string|false

Сохранить сформированное дерево в строку для ответа или дальнейшей обработки.

Аргумент / поведениеПолный контракт
Первый аргументnode=null сериализует весь документ; конкретный узел — только его поддерево без XML-декларации. Чужой документ/семейство → WRONG_DOCUMENT_ERR/TypeError.
ФорматXML-сериализация; options поддерживает LIBXML_NOEMPTYTAG и LIBXML_NOXMLDECL (NOXMLDECL в legacy с PHP 8.3). HTMLDocument.saveXml тоже выдаёт XML, а не HTML.
ВозвратСтрока; XML и legacy HTML могут вернуть false при отказе, modern saveHtml имеет string-контракт.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — строка
Получить результат сериализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
echo $document->saveXML($document->getElementsByTagName("product")->item(0)), "
";
Ожидаемый результат
<product>Tea</product>

Здесь сериализован один выбранный узел, не весь документ.

Убедиться, что изменённый товар попал в результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$document->getElementsByTagName("product")->item(0)->textContent = "Coffee & milk";
$output = $document->saveXML($document->getElementsByTagName("product")->item(0));
echo str_contains($output, "Coffee &amp; milk") ? "text escaped
" : "unexpected
";
Ожидаемый результат
text escaped

Сериализатор экранирует амперсанд. Получатель XML/HTML после разбора увидит исходный знак &.

Связанные операции: DOMDocument::save.

DOMDocument::saveСохранить сформированное дерево в файл для выгрузки.

PHP 5+ · dom

DOMDocument::save(string $filename, int $options = 0): int|false

Сохранить сформированное дерево в файл для выгрузки.

Аргумент / поведениеПолный контракт
Первый аргументИмя файла/поддерживаемого потока. Путь выбирает приложение; операция перезаписывает целевой файл.
ФорматXML-сериализация; options поддерживает LIBXML_NOEMPTYTAG и LIBXML_NOXMLDECL (NOXMLDECL в legacy с PHP 8.3). HTMLDocument.saveXml тоже выдаёт XML, а не HTML.
Возвратint числа записанных БАЙТОВ либо false. Проверяйте === false: 0 не равно отказу.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — записанный файл и число байтов
Сохранить result.xml и показать всё содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$path = __DIR__ . "/result.xml";
if ($document->save($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0"?>
<catalog><product>Tea</product></catalog>

Метод записывает именно выбранный файл. Его байты прочитаны обратно и показаны полностью: декларация/doctype, корень и товар. === false отличает отказ от допустимого нулевого размера. Папка примера изолирована.

Название с амперсандом в готовом файлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$document->getElementsByTagName("product")->item(0)->textContent = "Coffee & milk";
$path = __DIR__ . "/result.xml";
if ($document->save($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0"?>
<catalog><product>Coffee &amp; milk</product></catalog>

В дерево попала строка Coffee & milk, а файл содержит XML/HTML-запись Coffee & milk. Повторный разбор восстановит обычный знак &. Файл доступен в учебной папке после запуска.

Связанные операции: DOMDocument::saveXML.

DOMDocument::saveHTMLСохранить сформированное дерево в строку для ответа или дальнейшей обработки.

PHP 5+ · dom

DOMDocument::saveHTML(?DOMNode $node = null): string|false

Сохранить сформированное дерево в строку для ответа или дальнейшей обработки.

Аргумент / поведениеПолный контракт
Первый аргументnode=null сериализует весь документ; конкретный узел — только его поддерево без XML-декларации. Чужой документ/семейство → WRONG_DOCUMENT_ERR/TypeError.
ФорматHTML-сериализация учитывает HTML-элементы и void-теги.
ВозвратСтрока; XML и legacy HTML могут вернуть false при отказе, modern saveHtml имеет string-контракт.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — строка
Получить результат сериализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadHTML('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
echo $document->saveHTML($document->getElementsByTagName("p")->item(0)), "
";
Ожидаемый результат
<p>Tea</p>

Здесь сериализован один выбранный узел, не весь документ.

Убедиться, что изменённый товар попал в результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadHTML('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$document->getElementsByTagName("p")->item(0)->textContent = "Coffee & milk";
$output = $document->saveHTML($document->getElementsByTagName("p")->item(0));
echo str_contains($output, "Coffee &amp; milk") ? "text escaped
" : "unexpected
";
Ожидаемый результат
text escaped

Сериализатор экранирует амперсанд. Получатель XML/HTML после разбора увидит исходный знак &.

Связанные операции: DOMDocument::saveHTMLFile.

DOMDocument::saveHTMLFileСохранить сформированное дерево в файл для выгрузки.

PHP 5+ · dom

DOMDocument::saveHTMLFile(string $filename): int|false

Сохранить сформированное дерево в файл для выгрузки.

Аргумент / поведениеПолный контракт
Первый аргументИмя файла/поддерживаемого потока. Путь выбирает приложение; операция перезаписывает целевой файл.
ФорматHTML-сериализация учитывает HTML-элементы и void-теги.
Возвратint числа записанных БАЙТОВ либо false. Проверяйте === false: 0 не равно отказу.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — записанный файл и число байтов
Сохранить result.html и показать всё содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadHTML('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$path = __DIR__ . "/result.html";
if ($document->saveHTMLFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Catalog</title></head><body><p>Tea</p></body></html>

Метод записывает именно выбранный файл. Его байты прочитаны обратно и показаны полностью: декларация/doctype, корень и товар. === false отличает отказ от допустимого нулевого размера. Папка примера изолирована.

Название с амперсандом в готовом файлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadHTML('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$document->getElementsByTagName("p")->item(0)->textContent = "Coffee & milk";
$path = __DIR__ . "/result.html";
if ($document->saveHTMLFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Catalog</title></head><body><p>Coffee &amp; milk</p></body></html>

В дерево попала строка Coffee & milk, а файл содержит XML/HTML-запись Coffee & milk. Повторный разбор восстановит обычный знак &. Файл доступен в учебной папке после запуска.

Связанные операции: DOMDocument::saveHTML.

Dom\XMLDocument::saveXmlСохранить сформированное дерево в строку для ответа или дальнейшей обработки.

PHP 8.4+ · dom

Dom\XMLDocument::saveXml(?Dom\Node $node = null, int $options = 0): string|false

Сохранить сформированное дерево в строку для ответа или дальнейшей обработки.

Аргумент / поведениеПолный контракт
Первый аргументnode=null сериализует весь документ; конкретный узел — только его поддерево без XML-декларации. Чужой документ/семейство → WRONG_DOCUMENT_ERR/TypeError.
ФорматXML-сериализация; options поддерживает LIBXML_NOEMPTYTAG и LIBXML_NOXMLDECL (NOXMLDECL в legacy с PHP 8.3). HTMLDocument.saveXml тоже выдаёт XML, а не HTML.
ВозвратСтрока; XML и legacy HTML могут вернуть false при отказе, modern saveHtml имеет string-контракт.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — строка
Получить результат сериализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
echo $document->saveXml($document->getElementsByTagName("product")->item(0)), "
";
Ожидаемый результат
<product>Tea</product>

Здесь сериализован один выбранный узел, не весь документ.

Убедиться, что изменённый товар попал в результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$document->getElementsByTagName("product")->item(0)->textContent = "Coffee & milk";
$output = $document->saveXml($document->getElementsByTagName("product")->item(0));
echo str_contains($output, "Coffee &amp; milk") ? "text escaped
" : "unexpected
";
Ожидаемый результат
text escaped

Сериализатор экранирует амперсанд. Получатель XML/HTML после разбора увидит исходный знак &.

Связанные операции: Dom\XMLDocument::saveXmlFile.

Dom\XMLDocument::saveXmlFileСохранить сформированное дерево в файл для выгрузки.

PHP 8.4+ · dom

Dom\XMLDocument::saveXmlFile(string $filename, int $options = 0): int|false

Сохранить сформированное дерево в файл для выгрузки.

Аргумент / поведениеПолный контракт
Первый аргументИмя файла/поддерживаемого потока. Путь выбирает приложение; операция перезаписывает целевой файл.
ФорматXML-сериализация; options поддерживает LIBXML_NOEMPTYTAG и LIBXML_NOXMLDECL (NOXMLDECL в legacy с PHP 8.3). HTMLDocument.saveXml тоже выдаёт XML, а не HTML.
Возвратint числа записанных БАЙТОВ либо false. Проверяйте === false: 0 не равно отказу.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — записанный файл и число байтов
Сохранить result.xml и показать всё содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$path = __DIR__ . "/result.xml";
if ($document->saveXmlFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8"?>
<catalog><product>Tea</product></catalog>

Метод записывает именно выбранный файл. Его байты прочитаны обратно и показаны полностью: декларация/doctype, корень и товар. === false отличает отказ от допустимого нулевого размера. Папка примера изолирована.

Название с амперсандом в готовом файлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$document->getElementsByTagName("product")->item(0)->textContent = "Coffee & milk";
$path = __DIR__ . "/result.xml";
if ($document->saveXmlFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8"?>
<catalog><product>Coffee &amp; milk</product></catalog>

В дерево попала строка Coffee & milk, а файл содержит XML/HTML-запись Coffee & milk. Повторный разбор восстановит обычный знак &. Файл доступен в учебной папке после запуска.

Связанные операции: Dom\XMLDocument::saveXml.

Dom\HTMLDocument::saveXmlСохранить сформированное дерево в строку для ответа или дальнейшей обработки.

PHP 8.4+ · dom

Dom\HTMLDocument::saveXml(?Dom\Node $node = null, int $options = 0): string|false

Сохранить сформированное дерево в строку для ответа или дальнейшей обработки.

Аргумент / поведениеПолный контракт
Первый аргументnode=null сериализует весь документ; конкретный узел — только его поддерево без XML-декларации. Чужой документ/семейство → WRONG_DOCUMENT_ERR/TypeError.
ФорматXML-сериализация; options поддерживает LIBXML_NOEMPTYTAG и LIBXML_NOXMLDECL (NOXMLDECL в legacy с PHP 8.3). HTMLDocument.saveXml тоже выдаёт XML, а не HTML.
ВозвратСтрока; XML и legacy HTML могут вернуть false при отказе, modern saveHtml имеет string-контракт.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — строка
Получить результат сериализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
echo $document->saveXml($document->getElementsByTagName("p")->item(0)), "
";
Ожидаемый результат
<p xmlns="http://www.w3.org/1999/xhtml">Tea</p>

Здесь сериализован один выбранный узел, не весь документ.

Убедиться, что изменённый товар попал в результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$document->getElementsByTagName("p")->item(0)->textContent = "Coffee & milk";
$output = $document->saveXml($document->getElementsByTagName("p")->item(0));
echo str_contains($output, "Coffee &amp; milk") ? "text escaped
" : "unexpected
";
Ожидаемый результат
text escaped

Сериализатор экранирует амперсанд. Получатель XML/HTML после разбора увидит исходный знак &.

Связанные операции: Dom\HTMLDocument::saveXmlFile.

Dom\HTMLDocument::saveXmlFileСохранить сформированное дерево в файл для выгрузки.

PHP 8.4+ · dom

Dom\HTMLDocument::saveXmlFile(string $filename, int $options = 0): int|false

Сохранить сформированное дерево в файл для выгрузки.

Аргумент / поведениеПолный контракт
Первый аргументИмя файла/поддерживаемого потока. Путь выбирает приложение; операция перезаписывает целевой файл.
ФорматXML-сериализация; options поддерживает LIBXML_NOEMPTYTAG и LIBXML_NOXMLDECL (NOXMLDECL в legacy с PHP 8.3). HTMLDocument.saveXml тоже выдаёт XML, а не HTML.
Возвратint числа записанных БАЙТОВ либо false. Проверяйте === false: 0 не равно отказу.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — записанный файл и число байтов
Сохранить result.xml и показать всё содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$path = __DIR__ . "/result.xml";
if ($document->saveXmlFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Catalog</title></head><body><p>Tea</p></body></html>

Метод записывает именно выбранный файл. Его байты прочитаны обратно и показаны полностью: декларация/doctype, корень и товар. === false отличает отказ от допустимого нулевого размера. Папка примера изолирована.

Название с амперсандом в готовом файлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$document->getElementsByTagName("p")->item(0)->textContent = "Coffee & milk";
$path = __DIR__ . "/result.xml";
if ($document->saveXmlFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Catalog</title></head><body><p>Coffee &amp; milk</p></body></html>

В дерево попала строка Coffee & milk, а файл содержит XML/HTML-запись Coffee & milk. Повторный разбор восстановит обычный знак &. Файл доступен в учебной папке после запуска.

Связанные операции: Dom\HTMLDocument::saveXml.

Dom\HTMLDocument::saveHtmlСохранить сформированное дерево в строку для ответа или дальнейшей обработки.

PHP 8.4+ · dom

Dom\HTMLDocument::saveHtml(?Dom\Node $node = null): string

Сохранить сформированное дерево в строку для ответа или дальнейшей обработки.

Аргумент / поведениеПолный контракт
Первый аргументnode=null сериализует весь документ; конкретный узел — только его поддерево без XML-декларации. Чужой документ/семейство → WRONG_DOCUMENT_ERR/TypeError.
ФорматHTML-сериализация учитывает HTML-элементы и void-теги.
ВозвратСтрока; XML и legacy HTML могут вернуть false при отказе, modern saveHtml имеет string-контракт.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — строка
Получить результат сериализацииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
echo $document->saveHtml($document->getElementsByTagName("p")->item(0)), "
";
Ожидаемый результат
<p>Tea</p>

Здесь сериализован один выбранный узел, не весь документ.

Убедиться, что изменённый товар попал в результатСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$document->getElementsByTagName("p")->item(0)->textContent = "Coffee & milk";
$output = $document->saveHtml($document->getElementsByTagName("p")->item(0));
echo str_contains($output, "Coffee &amp; milk") ? "text escaped
" : "unexpected
";
Ожидаемый результат
text escaped

Сериализатор экранирует амперсанд. Получатель XML/HTML после разбора увидит исходный знак &.

Связанные операции: Dom\HTMLDocument::saveHtmlFile.

Dom\HTMLDocument::saveHtmlFileСохранить сформированное дерево в файл для выгрузки.

PHP 8.4+ · dom

Dom\HTMLDocument::saveHtmlFile(string $filename): int|false

Сохранить сформированное дерево в файл для выгрузки.

Аргумент / поведениеПолный контракт
Первый аргументИмя файла/поддерживаемого потока. Путь выбирает приложение; операция перезаписывает целевой файл.
ФорматHTML-сериализация учитывает HTML-элементы и void-теги.
Возвратint числа записанных БАЙТОВ либо false. Проверяйте === false: 0 не равно отказу.
Кодировка / форматированиеПолный документ сериализуется в его кодировке; сериализация отдельного узла использует UTF-8. formatOutput — форматирование, не сохранение исходных пробелов побайтно.
ГраницаСериализация не санитизирует опасные HTML URL/скрипты. DOM хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика.
Публикация файлаДля целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом.
Дерево → байты
  1. Узлы уже содержат проверенные данные
  2. save* выбирает XML либо HTML serializer
  3. Результат — записанный файл и число байтов
Сохранить result.html и показать всё содержимоеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$path = __DIR__ . "/result.html";
if ($document->saveHtmlFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>

Метод записывает именно выбранный файл. Его байты прочитаны обратно и показаны полностью: декларация/doctype, корень и товар. === false отличает отказ от допустимого нулевого размера. Папка примера изолирована.

Название с амперсандом в готовом файлеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createFromString('<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Tea</p></body></html>');
$document->getElementsByTagName("p")->item(0)->textContent = "Coffee & milk";
$path = __DIR__ . "/result.html";
if ($document->saveHtmlFile($path) === false) { throw new RuntimeException("Файл не записан"); }
echo file_get_contents($path);
Ожидаемый результат
<!DOCTYPE html><html><head><title>Catalog</title></head><body><p>Coffee &amp; milk</p></body></html>

В дерево попала строка Coffee & milk, а файл содержит XML/HTML-запись Coffee & milk. Повторный разбор восстановит обычный знак &. Файл доступен в учебной папке после запуска.

Связанные операции: Dom\HTMLDocument::saveHtml.

DOMDocument::normalizeDocumentПривести текстовые узлы всего старого DOM к согласованному виду после серии склеиваний и вставок.

PHP 5+ · dom

DOMDocument::normalizeDocument(): void

Привести текстовые узлы всего старого DOM к согласованному виду после серии склеиваний и вставок.

Аргумент / поведениеПолный контракт
Аргументы / возвратНет аргументов; void. Сливает соседние Text и убирает пустые Text в дереве.
ГраницаЭто не форматирование отступов, не Unicode NFC, не исправление произвольного XML и не XSD-валидация. Для одной ветки достаточно Node::normalize.
Нормализация дерева
  1. catalog: Text Tea + Text Coffee
  2. normalizeDocument объединяет соседний текст
  3. Содержимое TeaCoffee прежнее, объектов Text меньше
Слить соседние текстовые узлыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$root = $document->documentElement;
$root->appendChild($document->createTextNode("Tea"));
$root->appendChild($document->createTextNode("Coffee"));
$document->normalizeDocument();
echo $root->childNodes->length, ":", $root->textContent, "
";
Ожидаемый результат
1:TeaCoffee

Слова не получают пробел автоматически.

Не смешать нормализацию с форматированиемСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product><product>Coffee</product></catalog>');
$document->normalizeDocument();
echo $document->getElementsByTagName("product")->length, "
";
Ожидаемый результат
2

Два Element не сливаются, даже если стоят рядом.

Связанные операции: DOMNode::normalize.

DOMDocument::schemaValidateПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 5+ · dom

DOMDocument::schemaValidate(string $filename, int $flags = 0): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументКонтролируемый путь к файлу XSD 1.0. Не XSD 1.1.
Флаги$flags=0; LIBXML_SCHEMA_CREATE позволяет материализовать значения по умолчанию/фиксированные значения схемы. Это может изменить дерево.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidate("stock.xsd");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidate("stock.xsd");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: DOMDocument::relaxNGValidate.

DOMDocument::schemaValidateSourceПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 5+ · dom

DOMDocument::schemaValidateSource(string $source, int $flags = 0): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументСтрока со схемой XML Schema 1.0; XSD 1.1 не поддерживается.
Флаги$flags=0; LIBXML_SCHEMA_CREATE позволяет материализовать значения по умолчанию/фиксированные значения схемы. Это может изменить дерево.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidateSource('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="stock" type="xs:nonNegativeInteger"/></xs:schema>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidateSource('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="stock" type="xs:nonNegativeInteger"/></xs:schema>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: DOMDocument::relaxNGValidateSource.

DOMDocument::relaxNGValidateПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 5+ · dom

DOMDocument::relaxNGValidate(string $filename): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументКонтролируемый путь к файлу RNG в XML-синтаксисе.
ФлагиУ Relax NG дополнительного аргумента flags нет.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNGValidate("stock.rng");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNGValidate("stock.rng");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: DOMDocument::schemaValidate.

DOMDocument::relaxNGValidateSourceПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 5+ · dom

DOMDocument::relaxNGValidateSource(string $source): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументСтрока со схемой RNG в XML-синтаксисе, не compact RNC.
ФлагиУ Relax NG дополнительного аргумента flags нет.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNGValidateSource('<element xmlns="http://relaxng.org/ns/structure/1.0" name="stock"><data type="nonNegativeInteger" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/></element>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNGValidateSource('<element xmlns="http://relaxng.org/ns/structure/1.0" name="stock"><data type="nonNegativeInteger" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/></element>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: DOMDocument::schemaValidateSource.

Dom\Document::schemaValidateПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 8.4+ · dom

Dom\Document::schemaValidate(string $filename, int $flags = 0): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументКонтролируемый путь к файлу XSD 1.0. Не XSD 1.1.
Флаги$flags=0; LIBXML_SCHEMA_CREATE позволяет материализовать значения по умолчанию/фиксированные значения схемы. Это может изменить дерево.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidate("stock.xsd");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidate("stock.xsd");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: Dom\Document::relaxNgValidate.

Dom\Document::schemaValidateSourceПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 8.4+ · dom

Dom\Document::schemaValidateSource(string $source, int $flags = 0): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументСтрока со схемой XML Schema 1.0; XSD 1.1 не поддерживается.
Флаги$flags=0; LIBXML_SCHEMA_CREATE позволяет материализовать значения по умолчанию/фиксированные значения схемы. Это может изменить дерево.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidateSource('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="stock" type="xs:nonNegativeInteger"/></xs:schema>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->schemaValidateSource('<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="stock" type="xs:nonNegativeInteger"/></xs:schema>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: Dom\Document::relaxNgValidateSource.

Dom\Document::relaxNgValidateПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 8.4+ · dom

Dom\Document::relaxNgValidate(string $filename): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументКонтролируемый путь к файлу RNG в XML-синтаксисе.
ФлагиУ Relax NG дополнительного аргумента flags нет.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNgValidate("stock.rng");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNgValidate("stock.rng");
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: Dom\Document::schemaValidate.

Dom\Document::relaxNgValidateSourceПроверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

PHP 8.4+ · dom

Dom\Document::relaxNgValidateSource(string $source): bool

Проверить, что XML содержит допустимое значение остатка, до сохранения данных в вашей системе.

Аргумент / поведениеПолный контракт
Первый аргументСтрока со схемой RNG в XML-синтаксисе, не compact RNC.
ФлагиУ Relax NG дополнительного аргумента flags нет.
Возвратtrue при соответствии, false при ошибке схемы/данных. Подробности берите из libxml_get_errors; не выводите внутренние пути и сырой XML посетителю.
Два шагаУспешный load/createFromString означает синтаксически разобранный XML. schemaValidate/relaxNgValidate проверяет дополнительные правила структуры и типов.
Внешние ресурсыimport/include схемы могут читать другие ресурсы. Выбирайте свою схему и контролируйте resolver/доступ; не принимайте URL схемы от отправителя как доверенное правило.
Полнота проверкиСхема не проверяет остаток в вашей базе, права пользователя и конкурентное изменение заказа. После проверки XML нужны предметные правила.
Две границы приёма
  1. <stock>4</stock> → parser строит DOM
  2. Схема требует nonNegativeInteger
  3. 4 принято; -1 отклонено до обновления базы
Принять остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>4</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNgValidateSource('<element xmlns="http://relaxng.org/ns/structure/1.0" name="stock"><data type="nonNegativeInteger" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/></element>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток 4 принят

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отрицательный остаток по схемеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<stock>-1</stock>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->relaxNgValidateSource('<element xmlns="http://relaxng.org/ns/structure/1.0" name="stock"><data type="nonNegativeInteger" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/></element>');
    echo $valid ? "Остаток 4 принят\n" : "Остаток -1 отклонён: нужен целый остаток не меньше нуля\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Остаток -1 отклонён: нужен целый остаток не меньше нуля

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: Dom\Document::schemaValidateSource.

DOMDocument::validateПроверить XML по его DTD, если старый формат обмена описан именно этим способом.

PHP 5+ · dom

DOMDocument::validate(): bool

Проверить XML по его DTD, если старый формат обмена описан именно этим способом.

Аргумент / поведениеПолный контракт
Аргументы / возвратНет аргументов; bool. Используется DTD документа. Без DTD проверять нечего: получаем отказ/диагностику, а не автоматическое признание документа корректным.
ОграниченияDTD описывает структуру и некоторые виды атрибутов, но не заменяет XSD/RNG для богатых типов и не решает бизнес-проверки.
БезопасностьНедоверенная DTD может обращаться к внешним сущностям. Пример использует фиксированную внутреннюю DTD; не включайте resolveExternals/подстановку сущностей для случайных входящих файлов.
Ошибкиfalse и сообщения libxml. Сохраните/восстановите режим ошибок, очищайте буфер после обработки, особенно в долгоживущем worker.
Структурный договор
  1. DTD: catalog должен содержать product+
  2. catalog с product → true
  3. Пустой catalog → false
Принять документ по DTDСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE catalog [<!ELEMENT catalog (product+)><!ELEMENT product (#PCDATA)>]><catalog><product>Tea</product></catalog>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->validate();
    echo $valid ? "Заказ принят: есть обязательная позиция Tea\n" : "Заказ отклонён: DTD требует хотя бы один product\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Заказ принят: есть обязательная позиция Tea

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отсутствие обязательной позицииСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE catalog [<!ELEMENT catalog (product+)><!ELEMENT product (#PCDATA)>]><catalog/>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->validate();
    echo $valid ? "Заказ принят: есть обязательная позиция Tea\n" : "Заказ отклонён: DTD требует хотя бы один product\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Заказ отклонён: DTD требует хотя бы один product

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: DOMDocument::schemaValidateSource.

DOMDocument::xincludeСобрать управляемый XML-отчёт из нескольких локальных частей, когда формат требует XInclude.

PHP 5+ · dom

DOMDocument::xinclude(int $options = 0): int|false

Собрать управляемый XML-отчёт из нескольких локальных частей, когда формат требует XInclude.

Аргумент / поведениеПолный контракт
$options=0Битовая маска поддерживаемых libxml-флагов. Эта операция запускает отдельную обработку include после построения дерева.
ВозвратModern int: число подстановок, 0 если нечего менять; отказ отражается отрицательным результатом/диагностикой. Legacy int|false: число, -1 при части ошибок, false при отсутствии подстановок. Сравнивайте строго, не используйте одну проверку if(!$result).
Источникhref может читать локальные файлы и внешние URL. Не вызывайте xinclude на произвольном XML клиента. NONET не запрещает file:// и локальные пути.
DTDlibxml может автоматически разрешать сущности внутри подключаемого XML с DTD, что меняет ожидаемое поведение. Примеры используют свои части без DTD.
АльтернативаДля произвольных входящих XML лучше импортировать явно выбранные узлы через importNode, контролируя источник и размер.
Подстановка локальной части
  1. catalog содержит xi:include href=product.xml
  2. xinclude читает управляемый product.xml
  3. В дереве появился product Tea
Включить разрешённую часть отчётаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include href="product.xml"/></catalog>');
echo $document->xinclude(LIBXML_NONET), "
";
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
1
Tea

Fixture лежит в каталоге примера. Здесь нет обращения к сторонней сети.

Отличить отсутствие include от отказаСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$result = $document->xinclude(LIBXML_NONET);
echo $result === false || $result === 0 ? "nothing to include
" : "processed
";
Ожидаемый результат
nothing to include

Для общих обёрток над обоими API учитывайте разные значения «нет подстановок». Отдельно проверяйте -1.

Связанные операции: DOMDocument::importNode.

DOMDocument::createEntityReferenceСоздать ссылку на уже объявленную сущность XML, когда формат обмена сохраняет такие ссылки.

PHP 5+ · dom

DOMDocument::createEntityReference(string $name): DOMEntityReference|false

Создать ссылку на уже объявленную сущность XML, когда формат обмена сохраняет такие ссылки.

Аргумент / поведениеПолный контракт
$nameИмя сущности без & и ;. Недопустимое XML-имя → INVALID_CHARACTER_ERR; метод не объявляет DTD-сущность автоматически.
ВозвратEntityReference. Legacy разрешает вставить такую ссылку и сериализует &name;. Modern создаёт объект, но его стандартные mutators отклоняют вставку EntityReference с HIERARCHY_REQUEST_ERR; такие узлы могут присутствовать после XML-парсинга.
ВыборДля обычного текста создавайте Text. Не используйте сущности для обхода экранирования или загрузки внешних ресурсов.
Имя → ссылка
  1. DTD объявляет tea="Tea"
  2. createEntityReference("tea") создаёт ссылку
  3. В XML записывается &tea;, обработка определяется XML-получателем
Создать ссылку на внутреннюю сущностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<!DOCTYPE catalog [<!ENTITY tea "Tea">]><catalog/>');
$entity = $document->createEntityReference("tea");
$document->documentElement->appendChild($entity);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>&tea;</catalog>

DTD принадлежит приложению; внешняя подгрузка не используется.

Различить буквальный текст и сущностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$text = $document->createTextNode("&tea;");
$document->documentElement->appendChild($text);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>&amp;tea;</catalog>

Text сохраняет буквальные символы &tea;, не создаёт ссылку на сущность.

Связанные операции: DOMDocument::createTextNode.

Dom\XMLDocument::validateПроверить XML по его DTD, если старый формат обмена описан именно этим способом.

PHP 8.4+ · dom

Dom\XMLDocument::validate(): bool

Проверить XML по его DTD, если старый формат обмена описан именно этим способом.

Аргумент / поведениеПолный контракт
Аргументы / возвратНет аргументов; bool. Используется DTD документа. Без DTD проверять нечего: получаем отказ/диагностику, а не автоматическое признание документа корректным.
ОграниченияDTD описывает структуру и некоторые виды атрибутов, но не заменяет XSD/RNG для богатых типов и не решает бизнес-проверки.
БезопасностьНедоверенная DTD может обращаться к внешним сущностям. Пример использует фиксированную внутреннюю DTD; не включайте resolveExternals/подстановку сущностей для случайных входящих файлов.
Ошибкиfalse и сообщения libxml. Сохраните/восстановите режим ошибок, очищайте буфер после обработки, особенно в долгоживущем worker.
Структурный договор
  1. DTD: catalog должен содержать product+
  2. catalog с product → true
  3. Пустой catalog → false
Принять документ по DTDСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ELEMENT catalog (product+)><!ELEMENT product (#PCDATA)>]><catalog><product>Tea</product></catalog>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->validate();
    echo $valid ? "Заказ принят: есть обязательная позиция Tea\n" : "Заказ отклонён: DTD требует хотя бы один product\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Заказ принят: есть обязательная позиция Tea

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Отклонить отсутствие обязательной позицииСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ELEMENT catalog (product+)><!ELEMENT product (#PCDATA)>]><catalog/>');
$previous = libxml_use_internal_errors(true);
try {
    $valid = $document->validate();
    echo $valid ? "Заказ принят: есть обязательная позиция Tea\n" : "Заказ отклонён: DTD требует хотя бы один product\n";
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Заказ отклонён: DTD требует хотя бы один product

Сначала XML уже разобран синтаксически, затем схема проверяет дополнительный договор. Решение выводится только после этой проверки. Локальная схема входит в архив либо видна строкой непосредственно в коде; прежний режим libxml восстановлен в finally.

Связанные операции: Dom\Document::schemaValidateSource.

Dom\XMLDocument::xincludeСобрать управляемый XML-отчёт из нескольких локальных частей, когда формат требует XInclude.

PHP 8.4+ · dom

Dom\XMLDocument::xinclude(int $options = 0): int

Собрать управляемый XML-отчёт из нескольких локальных частей, когда формат требует XInclude.

Аргумент / поведениеПолный контракт
$options=0Битовая маска поддерживаемых libxml-флагов. Эта операция запускает отдельную обработку include после построения дерева.
ВозвратModern int: число подстановок, 0 если нечего менять; отказ отражается отрицательным результатом/диагностикой. Legacy int|false: число, -1 при части ошибок, false при отсутствии подстановок. Сравнивайте строго, не используйте одну проверку if(!$result).
Источникhref может читать локальные файлы и внешние URL. Не вызывайте xinclude на произвольном XML клиента. NONET не запрещает file:// и локальные пути.
DTDlibxml может автоматически разрешать сущности внутри подключаемого XML с DTD, что меняет ожидаемое поведение. Примеры используют свои части без DTD.
АльтернативаДля произвольных входящих XML лучше импортировать явно выбранные узлы через importNode, контролируя источник и размер.
Подстановка локальной части
  1. catalog содержит xi:include href=product.xml
  2. xinclude читает управляемый product.xml
  3. В дереве появился product Tea
Включить разрешённую часть отчётаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include href="product.xml"/></catalog>');
echo $document->xinclude(LIBXML_NONET), "
";
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
1
Tea

Fixture лежит в каталоге примера. Здесь нет обращения к сторонней сети.

Отличить отсутствие include от отказаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$result = $document->xinclude(LIBXML_NONET);
echo $result === false || $result === 0 ? "nothing to include
" : "processed
";
Ожидаемый результат
nothing to include

Для общих обёрток над обоими API учитывайте разные значения «нет подстановок». Отдельно проверяйте -1.

Связанные операции: Dom\Document::importNode.

Dom\XMLDocument::createEntityReferenceСоздать ссылку на уже объявленную сущность XML, когда формат обмена сохраняет такие ссылки.

PHP 8.4+ · dom

Dom\XMLDocument::createEntityReference(string $name): Dom\EntityReference

Создать ссылку на уже объявленную сущность XML, когда формат обмена сохраняет такие ссылки.

Аргумент / поведениеПолный контракт
$nameИмя сущности без & и ;. Недопустимое XML-имя → INVALID_CHARACTER_ERR; метод не объявляет DTD-сущность автоматически.
ВозвратEntityReference. Legacy разрешает вставить такую ссылку и сериализует &name;. Modern создаёт объект, но его стандартные mutators отклоняют вставку EntityReference с HIERARCHY_REQUEST_ERR; такие узлы могут присутствовать после XML-парсинга.
ВыборДля обычного текста создавайте Text. Не используйте сущности для обхода экранирования или загрузки внешних ресурсов.
Имя → ссылка
  1. DTD объявляет tea="Tea"
  2. createEntityReference("tea") создаёт ссылку
  3. В XML записывается &tea;, обработка определяется XML-получателем
Создать ссылку на внутреннюю сущностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<!DOCTYPE catalog [<!ENTITY tea "Tea">]><catalog/>');
$entity = $document->createEntityReference("tea");
try {
    $document->documentElement->appendChild($entity);
} catch (DOMException $error) {
    echo "entity reference cannot be inserted by modern mutator
";
}
echo $entity->nodeName, "
";
Ожидаемый результат
entity reference cannot be inserted by modern mutator
tea

DTD принадлежит приложению; внешняя подгрузка не используется.

Различить буквальный текст и сущностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$text = $document->createTextNode("&tea;");
$document->documentElement->appendChild($text);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>&amp;tea;</catalog>

Text сохраняет буквальные символы &tea;, не создаёт ссылку на сущность.

Связанные операции: Dom\Document::createTextNode.

DOMXPath::__constructПодготовить поиск по дереву каталога: выбрать товары, атрибуты или вычислить итог одним XPath-выражением.

PHP 5+ · dom

DOMXPath::__construct(DOMDocument $document, bool $registerNodeNS = true)

Подготовить поиск по дереву каталога: выбрать товары, атрибуты или вычислить итог одним XPath-выражением.

Аргумент / поведениеПолный контракт
$documentDOMDocument для legacy либо Dom\Document для modern. XPath связан с этим документом; нельзя подставлять узлы чужого документа как контекст.
$registerNodeNS=trueАвтоматически регистрировать namespace-префиксы контекстного узла. Для стабильного контракта можно отключить и зарегистрировать собственные префиксы вручную.
Результатnew создаёт XPath-объект. readonly document возвращает документ, registerNodeNamespaces управляет автоподхватом префиксов.
Версия / стандартЭто XPath 1.0 на libxml, не XPath 2/3. С PHP 8.4 клонирование XPath запрещено; создавайте отдельный объект для другого документа.
Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число
Создать поиск и получить доступный товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$available = $xpath->query('/catalog/product[@stock > 0]');
echo 'Можно заказать: ', $available->item(0)->textContent, "\n";
Ожидаемый результат
Можно заказать: Tea

new XPath связывает поисковый объект с документом. /catalog/product задаёт путь, @stock читает атрибут, [>0] отбирает доступную позицию. В этом фиксированном каталоге совпадение есть; для внешних данных проверяйте length и ошибки.

Убедиться, что поиск связан с документомСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
var_dump($xpath->document === $document);
echo $xpath->query("/catalog/product[@stock > 0]")->item(0)->textContent, "
";
Ожидаемый результат
bool(true)
Tea

XPath видит атрибут stock и выбирает нужную ветку без ручного foreach.

Связанные операции: DOMXPath::query, DOMXPath::evaluate, DOMXPath::registerNamespace.

DOMXPath::queryПолучить узлы XML, которые соответствуют пути и условиям отбора.

PHP 5+ · dom

DOMXPath::query(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed

Получить узлы XML, которые соответствуют пути и условиям отбора.

Аргумент / поведениеПолный контракт
$expressionXPath 1.0: / абсолютный путь, . текущий узел, // поиск потомков, @stock атрибут, [условие] фильтр. Данные строковых литералов экранируйте XPath::quote, а имена/структуру выражения задавайте своим кодом.
$contextNode=nullУзел того же документа для относительного выражения. .//product ограничивает поиск его потомками; //product начинает поиск от корня независимо от контекста.
$registerNodeNS=trueАвтоподхват namespace-префиксов контекста. При конфликте влияет ближайшее объявление; явная registerNamespace с собственным префиксом предсказуемее.
ВозвратСтатический NodeList; выражение скалярного типа даёт пустой список, для скаляра нужен evaluate.
ОшибкаНеверное выражение/контекст → false и диагностика libxml. В evaluate false также может быть корректным XPath boolean false: диагностируйте отдельно ошибки, не смешивайте эти случаи.
PHP callbacksНезарегистрированный callback → Error; имя handler не строка → TypeError; неподдержанный объект результата → Error. Исключение callback не становится пустым списком.
Стоимость// на большом дереве может обходить много узлов. Ограничивайте путь/контекст; повторный XPath не бесплатен. Результат хранит ссылки, не глубокие копии.
Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число
Выбрать доступные товарыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
foreach ($xpath->query("/catalog/product[@stock > 0]") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Tea

Условие использует известное числовое поле XML; внешний контракт данных всё равно должен быть проверен.

Работать относительно выбранной веткиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$root = $document->documentElement;
echo $xpath->query("product[@stock = 0]", $root)->item(0)->textContent, "
";
Ожидаемый результат
Coffee

Относительный путь product начинается от переданного catalog. Нет необходимости повторять полный путь.

Связанные операции: DOMXPath::evaluate, DOMXPath::quote, DOMXPath::registerNamespace.

DOMXPath::evaluateВычислить строку/число/логическое условие по XML.

PHP 5+ · dom

DOMXPath::evaluate(string $expression, ?DOMNode $contextNode = null, bool $registerNodeNS = true): mixed

Вычислить строку/число/логическое условие по XML.

Аргумент / поведениеПолный контракт
$expressionXPath 1.0: / абсолютный путь, . текущий узел, // поиск потомков, @stock атрибут, [условие] фильтр. Данные строковых литералов экранируйте XPath::quote, а имена/структуру выражения задавайте своим кодом.
$contextNode=nullУзел того же документа для относительного выражения. .//product ограничивает поиск его потомками; //product начинает поиск от корня независимо от контекста.
$registerNodeNS=trueАвтоподхват namespace-префиксов контекста. При конфликте влияет ближайшее объявление; явная registerNamespace с собственным префиксом предсказуемее.
ВозвратСтрока/float/bool либо NodeList в соответствии с типом XPath-результата.
ОшибкаНеверное выражение/контекст → false и диагностика libxml. В evaluate false также может быть корректным XPath boolean false: диагностируйте отдельно ошибки, не смешивайте эти случаи.
PHP callbacksНезарегистрированный callback → Error; имя handler не строка → TypeError; неподдержанный объект результата → Error. Исключение callback не становится пустым списком.
Стоимость// на большом дереве может обходить много узлов. Ограничивайте путь/контекст; повторный XPath не бесплатен. Результат хранит ссылки, не глубокие копии.
Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число
Выбрать доступные товарыСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
echo $xpath->evaluate("string(/catalog/product[@stock > 0])"), "
";
Ожидаемый результат
Tea

Условие использует известное числовое поле XML; внешний контракт данных всё равно должен быть проверен.

Работать относительно выбранной веткиСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$root = $document->documentElement;
echo $xpath->evaluate("sum(product/@stock)", $root), "
";
Ожидаемый результат
4

Относительный путь product начинается от переданного catalog. Нет необходимости повторять полный путь.

Связанные операции: DOMXPath::query, DOMXPath::quote, DOMXPath::registerNamespace.

DOMXPath::registerNamespaceНайти XML-элементы с пространством имён, включая default namespace без префикса в исходном файле.

PHP 5+ · dom

DOMXPath::registerNamespace(string $prefix, string $namespace): bool

Найти XML-элементы с пространством имён, включая default namespace без префикса в исходном файле.

Аргумент / поведениеПолный контракт
$prefix / $namespaceСобственный непустой префикс XPath и URI. Он не обязан совпадать с префиксом исходного XML; URI должен совпадать точно.
Возвратbool успешной регистрации. Повторная регистрация того же префикса меняет его связь для этого XPath-объекта.
Default namespaceВ XPath 1.0 имя без префикса выбирает элементы без namespace. Поэтому //product не находит product из xmlns="urn:catalog"; зарегистрируйте c и используйте //c:product.
ОбластьРегистрация не меняет исходный XML, не добавляет xmlns и не выполняет HTTP-запрос к URI.
Один URI, разные короткие имена
  1. XML: xmlns="urn:catalog"
  2. XPath: registerNamespace("c", "urn:catalog")
  3. /c:catalog/c:product выбирает Tea
Найти элемент default namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns="urn:catalog"><product>Tea</product></catalog>');
$xpath = new DOMXPath($document);
$xpath->registerNamespace("c", "urn:catalog");
echo $xpath->query("/c:catalog/c:product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Префикс c существует только в выражении, но указывает на тот же URI.

Показать причину пустого результата без namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog xmlns="urn:catalog"><product>Tea</product></catalog>');
$xpath = new DOMXPath($document);
echo $xpath->query("//product")->length, "
";
$xpath->registerNamespace("feed", "urn:catalog");
echo $xpath->query("//feed:product")->length, "
";
Ожидаемый результат
0
1

Совпадения текста имени недостаточно, если элемент находится в пространстве имён.

Связанные операции: DOMXPath::query, DOMXPath::evaluate.

DOMXPath::quoteВставить искомое название с кавычками в XPath как строковое значение, сохранив границу между данными и выражением.

PHP 8.4+ · dom

static DOMXPath::quote(string $str): string

Вставить искомое название с кавычками в XPath как строковое значение, сохранив границу между данными и выражением.

Аргумент / поведениеПолный контракт
$strЛюбая строка значения. Метод возвращает XPath-литерал с подходящими кавычками или concat для смешанных кавычек.
ВозвратСтрока готового литерала, уже с необходимым оформлением. Не добавляйте вокруг результата ещё одну пару кавычек.
ГраницаЭто не экранирование HTML/SQL/регулярного выражения и не способ сделать безопасным имя тега/произвольное XPath-выражение. Структура запроса задаётся доверенным кодом.
ВерсияPHP 8.4+, статический вызов ::quote. Для старых версий нужна проверенная реализация XPath1.0 quoting, а не addslashes.
Данные остаются данными
  1. Искомая строка содержит одинарные и двойные кавычки
  2. quote формирует корректный литерал/concat
  3. XPath сравнивает значение, не выполняет часть строки как условие
Передать название в XPathСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$value = "Tea";
$query = "/catalog/product[text()=" . DOMXPath::quote($value) . "]";
echo $xpath->query($query)->item(0)->textContent, "
";
Ожидаемый результат
Tea

Здесь значение простое; тот же код работает с кавычками.

Прочитать строку с двумя видами кавычек после XPathСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$value = 'Tea "special" and \'coffee\'';
$literal = DOMXPath::quote($value);
echo $xpath->evaluate("string(" . $literal . ")"), "\n";
Ожидаемый результат
Tea "special" and 'coffee'

quote создаёт литерал XPath, включая concat при необходимости. evaluate возвращает исходное название с обеими кавычками — теперь результат виден целиком, а не только равенство строк.

Связанные операции: DOMXPath::query, DOMXPath::evaluate.

DOMXPath::registerPhpFunctionsДобавить в контролируемый XPath ограниченный набор PHP-преобразований, если штатных XPath-функций недостаточно.

PHP 5+ · dom

DOMXPath::registerPhpFunctions(string|array|null $restrict = null): void

Добавить в контролируемый XPath ограниченный набор PHP-преобразований, если штатных XPath-функций недостаточно.

Аргумент / поведениеПолный контракт
$restrict=nullnull разрешает все доступные PHP-функции — слишком широкое разрешение для обычного импорта. Передавайте имя, список имён или (с PHP8.4) alias=>callable.
ВызовНужно registerNamespace("php", "http://php.net/xpath"). php:function может передавать node-set как массив DOM-узлов; php:functionString преобразует node-set к строковому представлению.
Возврат / ошибкиvoid; неверное имя callback → ValueError, не-callable → TypeError с PHP8.4. Ошибки выполнения возникают при evaluate/query.
БезопасностьXPath с callbacks исполняет PHP. Выражение и список функций должны быть доверенными; не разрешайте системные/сетевые/файловые функции ради универсальности.
ВыборДля понятного публичного имени в своём URI используйте registerPhpFunctionNS; для простого sum/concat/string достаточно встроенного XPath.
Контролируемый callback
  1. registerPhpFunctions разрешает только strtoupper
  2. XPath вызывает php:functionString для текста Tea
  3. PHP возвращает TEA обратно в XPath и затем в переменную
Разрешить ровно одно преобразованиеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$xpath->registerNamespace("php", "http://php.net/xpath");
$xpath->registerPhpFunctions(["strtoupper"]);
echo $xpath->evaluate('php:functionString("strtoupper", /catalog/product[1])'), "
";
Ожидаемый результат
TEA

strtoupper здесь используется для ASCII Tea; для многоязычного текста выберите mb_strtoupper с нужным окружением.

Скрыть реализацию за именем callbackСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$xpath->registerNamespace("php", "http://php.net/xpath");
$xpath->registerPhpFunctions(["label" => static fn (string $value): string => "Item: " . $value]);
echo $xpath->evaluate('php:functionString("label", /catalog/product[1])'), "
";
Ожидаемый результат
Item: Tea

Массив alias=>callable требует PHP8.4. Внешнему выражению не открывается весь PHP API.

Связанные операции: DOMXPath::registerPhpFunctionNS, DOMXPath::evaluate.

DOMXPath::registerPhpFunctionNSДобавить предметную функцию XPath с понятным именем, например превратить список выбранных товаров в заголовок отчёта.

PHP 8.4+ · dom

DOMXPath::registerPhpFunctionNS(string $namespaceURI, string $name, callable $callable): void

Добавить предметную функцию XPath с понятным именем, например превратить список выбранных товаров в заголовок отчёта.

Аргумент / поведениеПолный контракт
$namespaceURI / $nameURI функции и локальное имя без префикса; некорректное имя → ValueError. Дополнительно зарегистрируйте префикс через registerNamespace.
$callableВызываемая PHP-функция/замыкание. Node-set приходит как array DOM-узлов, а не строка XML и не NodeList.
Возврат / ошибкиРегистрация возвращает void. Невызываемое значение → TypeError; ошибки callback проявятся во время XPath-вызова. У метода нет аргументов options/overrideEncoding: такие строки в старом manual — посторонняя ошибка документации.
ГраницаНе отдавайте недоверенному пользователю произвольный XPath с доступом к callable. Функция должна иметь узкую задачу и предсказуемые побочные эффекты.
Из XPath в PHP и обратно
  1. r:label(/catalog/product[1])
  2. PHP callback получает массив из одного Element Tea
  3. Возвращает Item: Tea; evaluate получает строку
Обработать переданный node-setСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$xpath->registerNamespace("r", "urn:report");
$xpath->registerPhpFunctionNS("urn:report", "label", static fn (array $nodes): string => "Item: " . $nodes[0]->textContent);
echo $xpath->evaluate("r:label(/catalog/product[1])"), "
";
Ожидаемый результат
Item: Tea

При неизвестном наборе callback обязан обработать пустой массив; следующий пример показывает эту границу.

Принять пустой результат выбораСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new DOMXPath($document);
$xpath->registerNamespace("r", "urn:report");
$xpath->registerPhpFunctionNS("urn:report", "label", static fn (array $nodes): string => $nodes === [] ? "No items" : $nodes[0]->textContent);
echo $xpath->evaluate("r:label(/catalog/missing)"), "
";
Ожидаемый результат
No items

Отсутствие узлов обработано внутри callable, поэтому нет обращения к отсутствующему индексу 0.

Связанные операции: DOMXPath::registerNamespace, DOMXPath::registerPhpFunctions.

Dom\XPath::__constructПодготовить поиск по дереву каталога: выбрать товары, атрибуты или вычислить итог одним XPath-выражением.

PHP 8.4+ · dom

Dom\XPath::__construct(Dom\Document $document, bool $registerNodeNS = true)

Подготовить поиск по дереву каталога: выбрать товары, атрибуты или вычислить итог одним XPath-выражением.

Аргумент / поведениеПолный контракт
$documentDOMDocument для legacy либо Dom\Document для modern. XPath связан с этим документом; нельзя подставлять узлы чужого документа как контекст.
$registerNodeNS=trueАвтоматически регистрировать namespace-префиксы контекстного узла. Для стабильного контракта можно отключить и зарегистрировать собственные префиксы вручную.
Результатnew создаёт XPath-объект. readonly document возвращает документ, registerNodeNamespaces управляет автоподхватом префиксов.
Версия / стандартЭто XPath 1.0 на libxml, не XPath 2/3. С PHP 8.4 клонирование XPath запрещено; создавайте отдельный объект для другого документа.
Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число
Создать поиск и получить доступный товарСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$available = $xpath->query('/catalog/product[@stock > 0]');
echo 'Можно заказать: ', $available->item(0)->textContent, "\n";
Ожидаемый результат
Можно заказать: Tea

new XPath связывает поисковый объект с документом. /catalog/product задаёт путь, @stock читает атрибут, [>0] отбирает доступную позицию. В этом фиксированном каталоге совпадение есть; для внешних данных проверяйте length и ошибки.

Убедиться, что поиск связан с документомСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
var_dump($xpath->document === $document);
echo $xpath->query("/catalog/product[@stock > 0]")->item(0)->textContent, "
";
Ожидаемый результат
bool(true)
Tea

XPath видит атрибут stock и выбирает нужную ветку без ручного foreach.

Связанные операции: Dom\XPath::query, Dom\XPath::evaluate, Dom\XPath::registerNamespace.

Dom\XPath::queryПолучить узлы XML, которые соответствуют пути и условиям отбора.

PHP 8.4+ · dom

Dom\XPath::query(string $expression, ?Dom\Node $contextNode = null, bool $registerNodeNS = true): Dom\NodeList

Получить узлы XML, которые соответствуют пути и условиям отбора.

Аргумент / поведениеПолный контракт
$expressionXPath 1.0: / абсолютный путь, . текущий узел, // поиск потомков, @stock атрибут, [условие] фильтр. Данные строковых литералов экранируйте XPath::quote, а имена/структуру выражения задавайте своим кодом.
$contextNode=nullУзел того же документа для относительного выражения. .//product ограничивает поиск его потомками; //product начинает поиск от корня независимо от контекста.
$registerNodeNS=trueАвтоподхват namespace-префиксов контекста. При конфликте влияет ближайшее объявление; явная registerNamespace с собственным префиксом предсказуемее.
ВозвратСтатический NodeList; выражение скалярного типа даёт пустой список, для скаляра нужен evaluate.
ОшибкаНеверное выражение/контекст → false и диагностика libxml. В evaluate false также может быть корректным XPath boolean false: диагностируйте отдельно ошибки, не смешивайте эти случаи.
PHP callbacksНезарегистрированный callback → Error; имя handler не строка → TypeError; неподдержанный объект результата → Error. Исключение callback не становится пустым списком.
Стоимость// на большом дереве может обходить много узлов. Ограничивайте путь/контекст; повторный XPath не бесплатен. Результат хранит ссылки, не глубокие копии.
Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число
Выбрать доступные товарыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
foreach ($xpath->query("/catalog/product[@stock > 0]") as $product) {
    echo $product->textContent, "
";
}
Ожидаемый результат
Tea

Условие использует известное числовое поле XML; внешний контракт данных всё равно должен быть проверен.

Работать относительно выбранной веткиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$root = $document->documentElement;
echo $xpath->query("product[@stock = 0]", $root)->item(0)->textContent, "
";
Ожидаемый результат
Coffee

Относительный путь product начинается от переданного catalog. Нет необходимости повторять полный путь.

Связанные операции: Dom\XPath::evaluate, Dom\XPath::quote, Dom\XPath::registerNamespace.

Dom\XPath::evaluateВычислить строку/число/логическое условие по XML.

PHP 8.4+ · dom

Dom\XPath::evaluate(string $expression, ?Dom\Node $contextNode = null, bool $registerNodeNS = true): Dom\NodeList|string|float|bool|null

Вычислить строку/число/логическое условие по XML.

Аргумент / поведениеПолный контракт
$expressionXPath 1.0: / абсолютный путь, . текущий узел, // поиск потомков, @stock атрибут, [условие] фильтр. Данные строковых литералов экранируйте XPath::quote, а имена/структуру выражения задавайте своим кодом.
$contextNode=nullУзел того же документа для относительного выражения. .//product ограничивает поиск его потомками; //product начинает поиск от корня независимо от контекста.
$registerNodeNS=trueАвтоподхват namespace-префиксов контекста. При конфликте влияет ближайшее объявление; явная registerNamespace с собственным префиксом предсказуемее.
ВозвратСтрока/float/bool либо NodeList в соответствии с типом XPath-результата.
ОшибкаНеверное выражение/контекст → false и диагностика libxml. В evaluate false также может быть корректным XPath boolean false: диагностируйте отдельно ошибки, не смешивайте эти случаи.
PHP callbacksНезарегистрированный callback → Error; имя handler не строка → TypeError; неподдержанный объект результата → Error. Исключение callback не становится пустым списком.
Стоимость// на большом дереве может обходить много узлов. Ограничивайте путь/контекст; повторный XPath не бесплатен. Результат хранит ссылки, не глубокие копии.
Путь в дереве
  1. /catalog/product выбирает товары
  2. [@stock > 0] оставляет товар с положительным остатком
  3. query → узлы; evaluate(count(...)) → число
Выбрать доступные товарыСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
echo $xpath->evaluate("string(/catalog/product[@stock > 0])"), "
";
Ожидаемый результат
Tea

Условие использует известное числовое поле XML; внешний контракт данных всё равно должен быть проверен.

Работать относительно выбранной веткиСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$root = $document->documentElement;
echo $xpath->evaluate("sum(product/@stock)", $root), "
";
Ожидаемый результат
4

Относительный путь product начинается от переданного catalog. Нет необходимости повторять полный путь.

Связанные операции: Dom\XPath::query, Dom\XPath::quote, Dom\XPath::registerNamespace.

Dom\XPath::registerNamespaceНайти XML-элементы с пространством имён, включая default namespace без префикса в исходном файле.

PHP 8.4+ · dom

Dom\XPath::registerNamespace(string $prefix, string $namespace): bool

Найти XML-элементы с пространством имён, включая default namespace без префикса в исходном файле.

Аргумент / поведениеПолный контракт
$prefix / $namespaceСобственный непустой префикс XPath и URI. Он не обязан совпадать с префиксом исходного XML; URI должен совпадать точно.
Возвратbool успешной регистрации. Повторная регистрация того же префикса меняет его связь для этого XPath-объекта.
Default namespaceВ XPath 1.0 имя без префикса выбирает элементы без namespace. Поэтому //product не находит product из xmlns="urn:catalog"; зарегистрируйте c и используйте //c:product.
ОбластьРегистрация не меняет исходный XML, не добавляет xmlns и не выполняет HTTP-запрос к URI.
Один URI, разные короткие имена
  1. XML: xmlns="urn:catalog"
  2. XPath: registerNamespace("c", "urn:catalog")
  3. /c:catalog/c:product выбирает Tea
Найти элемент default namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns="urn:catalog"><product>Tea</product></catalog>');
$xpath = new Dom\XPath($document);
$xpath->registerNamespace("c", "urn:catalog");
echo $xpath->query("/c:catalog/c:product")->item(0)->textContent, "
";
Ожидаемый результат
Tea

Префикс c существует только в выражении, но указывает на тот же URI.

Показать причину пустого результата без namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog xmlns="urn:catalog"><product>Tea</product></catalog>');
$xpath = new Dom\XPath($document);
echo $xpath->query("//product")->length, "
";
$xpath->registerNamespace("feed", "urn:catalog");
echo $xpath->query("//feed:product")->length, "
";
Ожидаемый результат
0
1

Совпадения текста имени недостаточно, если элемент находится в пространстве имён.

Связанные операции: Dom\XPath::query, Dom\XPath::evaluate.

Dom\XPath::quoteВставить искомое название с кавычками в XPath как строковое значение, сохранив границу между данными и выражением.

PHP 8.4+ · dom

static Dom\XPath::quote(string $str): string

Вставить искомое название с кавычками в XPath как строковое значение, сохранив границу между данными и выражением.

Аргумент / поведениеПолный контракт
$strЛюбая строка значения. Метод возвращает XPath-литерал с подходящими кавычками или concat для смешанных кавычек.
ВозвратСтрока готового литерала, уже с необходимым оформлением. Не добавляйте вокруг результата ещё одну пару кавычек.
ГраницаЭто не экранирование HTML/SQL/регулярного выражения и не способ сделать безопасным имя тега/произвольное XPath-выражение. Структура запроса задаётся доверенным кодом.
ВерсияPHP 8.4+, статический вызов ::quote. Для старых версий нужна проверенная реализация XPath1.0 quoting, а не addslashes.
Данные остаются данными
  1. Искомая строка содержит одинарные и двойные кавычки
  2. quote формирует корректный литерал/concat
  3. XPath сравнивает значение, не выполняет часть строки как условие
Передать название в XPathСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$value = "Tea";
$query = "/catalog/product[text()=" . Dom\XPath::quote($value) . "]";
echo $xpath->query($query)->item(0)->textContent, "
";
Ожидаемый результат
Tea

Здесь значение простое; тот же код работает с кавычками.

Прочитать строку с двумя видами кавычек после XPathСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$value = 'Tea "special" and \'coffee\'';
$literal = Dom\XPath::quote($value);
echo $xpath->evaluate("string(" . $literal . ")"), "\n";
Ожидаемый результат
Tea "special" and 'coffee'

quote создаёт литерал XPath, включая concat при необходимости. evaluate возвращает исходное название с обеими кавычками — теперь результат виден целиком, а не только равенство строк.

Связанные операции: Dom\XPath::query, Dom\XPath::evaluate.

Dom\XPath::registerPhpFunctionsДобавить в контролируемый XPath ограниченный набор PHP-преобразований, если штатных XPath-функций недостаточно.

PHP 8.4+ · dom

Dom\XPath::registerPhpFunctions(array|string|null $restrict = null): void

Добавить в контролируемый XPath ограниченный набор PHP-преобразований, если штатных XPath-функций недостаточно.

Аргумент / поведениеПолный контракт
$restrict=nullnull разрешает все доступные PHP-функции — слишком широкое разрешение для обычного импорта. Передавайте имя, список имён или (с PHP8.4) alias=>callable.
ВызовНужно registerNamespace("php", "http://php.net/xpath"). php:function может передавать node-set как массив DOM-узлов; php:functionString преобразует node-set к строковому представлению.
Возврат / ошибкиvoid; неверное имя callback → ValueError, не-callable → TypeError с PHP8.4. Ошибки выполнения возникают при evaluate/query.
БезопасностьXPath с callbacks исполняет PHP. Выражение и список функций должны быть доверенными; не разрешайте системные/сетевые/файловые функции ради универсальности.
ВыборДля понятного публичного имени в своём URI используйте registerPhpFunctionNS; для простого sum/concat/string достаточно встроенного XPath.
Контролируемый callback
  1. registerPhpFunctions разрешает только strtoupper
  2. XPath вызывает php:functionString для текста Tea
  3. PHP возвращает TEA обратно в XPath и затем в переменную
Разрешить ровно одно преобразованиеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$xpath->registerNamespace("php", "http://php.net/xpath");
$xpath->registerPhpFunctions(["strtoupper"]);
echo $xpath->evaluate('php:functionString("strtoupper", /catalog/product[1])'), "
";
Ожидаемый результат
TEA

strtoupper здесь используется для ASCII Tea; для многоязычного текста выберите mb_strtoupper с нужным окружением.

Скрыть реализацию за именем callbackСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$xpath->registerNamespace("php", "http://php.net/xpath");
$xpath->registerPhpFunctions(["label" => static fn (string $value): string => "Item: " . $value]);
echo $xpath->evaluate('php:functionString("label", /catalog/product[1])'), "
";
Ожидаемый результат
Item: Tea

Массив alias=>callable требует PHP8.4. Внешнему выражению не открывается весь PHP API.

Связанные операции: Dom\XPath::registerPhpFunctionNS, Dom\XPath::evaluate.

Dom\XPath::registerPhpFunctionNSДобавить предметную функцию XPath с понятным именем, например превратить список выбранных товаров в заголовок отчёта.

PHP 8.4+ · dom

Dom\XPath::registerPhpFunctionNS(string $namespaceURI, string $name, callable $callable): void

Добавить предметную функцию XPath с понятным именем, например превратить список выбранных товаров в заголовок отчёта.

Аргумент / поведениеПолный контракт
$namespaceURI / $nameURI функции и локальное имя без префикса; некорректное имя → ValueError. Дополнительно зарегистрируйте префикс через registerNamespace.
$callableВызываемая PHP-функция/замыкание. Node-set приходит как array DOM-узлов, а не строка XML и не NodeList.
Возврат / ошибкиРегистрация возвращает void. Невызываемое значение → TypeError; ошибки callback проявятся во время XPath-вызова. У метода нет аргументов options/overrideEncoding: такие строки в старом manual — посторонняя ошибка документации.
ГраницаНе отдавайте недоверенному пользователю произвольный XPath с доступом к callable. Функция должна иметь узкую задачу и предсказуемые побочные эффекты.
Из XPath в PHP и обратно
  1. r:label(/catalog/product[1])
  2. PHP callback получает массив из одного Element Tea
  3. Возвращает Item: Tea; evaluate получает строку
Обработать переданный node-setСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$xpath->registerNamespace("r", "urn:report");
$xpath->registerPhpFunctionNS("urn:report", "label", static fn (array $nodes): string => "Item: " . $nodes[0]->textContent);
echo $xpath->evaluate("r:label(/catalog/product[1])"), "
";
Ожидаемый результат
Item: Tea

При неизвестном наборе callback обязан обработать пустой массив; следующий пример показывает эту границу.

Принять пустой результат выбораСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product stock="4">Tea</product><product stock="0">Coffee</product></catalog>');
$xpath = new Dom\XPath($document);
$xpath->registerNamespace("r", "urn:report");
$xpath->registerPhpFunctionNS("urn:report", "label", static fn (array $nodes): string => $nodes === [] ? "No items" : $nodes[0]->textContent);
echo $xpath->evaluate("r:label(/catalog/missing)"), "
";
Ожидаемый результат
No items

Отсутствие узлов обработано внутри callable, поэтому нет обращения к отсутствующему индексу 0.

Связанные операции: Dom\XPath::registerNamespace, Dom\XPath::registerPhpFunctions.

dom_import_simplexmlПолучить DOM-представление уже разобранного SimpleXML, когда требуется операция над деревом, которой нет в кратком API.

PHP 5+ · dom

dom_import_simplexml(object $node): DOMAttr|DOMElement

Получить DOM-представление уже разобранного SimpleXML, когда требуется операция над деревом, которой нет в кратком API.

Аргумент / поведениеПолный контракт
$nodeОбъект SimpleXMLElement, представляющий элемент либо атрибут; передача произвольного объекта не является XML-конверсией.
ВозвратAttr или Element выбранного семейства. Это связанное представление той же libxml-структуры, не независимая глубокая копия.
ПоследствияИзменение DOM отражается в SimpleXML. Для независимого результата импортируйте узел в новый документ через importNode(deep:true).
СовместимостьВыберите старый либо современный мост последовательно. Нельзя бездумно смешивать wrapper families одного libxml-документа; modern мост доступен с PHP8.4.
ОшибкиНеподходящий объект/непредставимый узел → TypeError/ValueError/Error в зависимости от причины; заранее работайте с реальным выбранным элементом.
Два интерфейса одного дерева
  1. SimpleXML: product Tea
  2. import_simplexml → DOM Element
  3. DOM.textContent = Coffee → SimpleXML тоже читает Coffee
Изменить XML через DOM-представлениеСамостоятельный пример
<?php
declare(strict_types=1);

$xml = simplexml_load_string("<product>Tea</product>");
$node = dom_import_simplexml($xml);
$node->textContent = "Coffee";
echo (string) $xml, "
";
Ожидаемый результат
Coffee

Здесь не было повторного json/XML-кодирования: оба объекта смотрят на одно дерево.

Добавить поле через DOM и прочитать из SimpleXMLСамостоятельный пример
<?php
declare(strict_types=1);

$xml = simplexml_load_string("<product/>");
$node = dom_import_simplexml($xml);
$node->setAttribute("stock", "4");
echo (string) $xml["stock"], "
";
Ожидаемый результат
4

Добавление Attr стало видно в исходном SimpleXML-объекте.

Связанные операции: DOMDocument::importNode.

Dom\import_simplexmlПолучить DOM-представление уже разобранного SimpleXML, когда требуется операция над деревом, которой нет в кратком API.

PHP 8.4+ · dom

Dom\import_simplexml(object $node): Dom\Attr|Dom\Element

Получить DOM-представление уже разобранного SimpleXML, когда требуется операция над деревом, которой нет в кратком API.

Аргумент / поведениеПолный контракт
$nodeОбъект SimpleXMLElement, представляющий элемент либо атрибут; передача произвольного объекта не является XML-конверсией.
ВозвратAttr или Element выбранного семейства. Это связанное представление той же libxml-структуры, не независимая глубокая копия.
ПоследствияИзменение DOM отражается в SimpleXML. Для независимого результата импортируйте узел в новый документ через importNode(deep:true).
СовместимостьВыберите старый либо современный мост последовательно. Нельзя бездумно смешивать wrapper families одного libxml-документа; modern мост доступен с PHP8.4.
ОшибкиНеподходящий объект/непредставимый узел → TypeError/ValueError/Error в зависимости от причины; заранее работайте с реальным выбранным элементом.
Два интерфейса одного дерева
  1. SimpleXML: product Tea
  2. import_simplexml → DOM Element
  3. DOM.textContent = Coffee → SimpleXML тоже читает Coffee
Изменить XML через DOM-представлениеСамостоятельный пример
<?php
declare(strict_types=1);

$xml = simplexml_load_string("<product>Tea</product>");
$node = Dom\import_simplexml($xml);
$node->textContent = "Coffee";
echo (string) $xml, "
";
Ожидаемый результат
Coffee

Здесь не было повторного json/XML-кодирования: оба объекта смотрят на одно дерево.

Добавить поле через DOM и прочитать из SimpleXMLСамостоятельный пример
<?php
declare(strict_types=1);

$xml = simplexml_load_string("<product/>");
$node = Dom\import_simplexml($xml);
$node->setAttribute("stock", "4");
echo (string) $xml["stock"], "
";
Ожидаемый результат
4

Добавление Attr стало видно в исходном SimpleXML-объекте.

Связанные операции: Dom\Document::importNode.

DOMElement::insertAdjacentElementДобавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

PHP 8.3+ · dom

DOMElement::insertAdjacentElement(string $where, DOMElement $element): ?DOMElement

Добавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

Аргумент / поведениеПолный контракт
$whereModern: enum Dom\AdjacentPosition. Legacy: строка beforebegin, afterbegin, beforeend, afterend (с PHP8.3). Первые/последние позиции вокруг элемента, средние — внутри него.
Второй аргументElement того же семейства. Уже вставленный узел переносится, а не копируется; для копии cloneNode/importNode.
ВозвратВставленный Element либо null, если для внешней позиции нет подходящего родителя.
ОшибкиНеверная legacy позиция → SYNTAX_ERR; в modern неверный тип позиции → TypeError. Нарушение дерева/чужой документ legacy → DOMException. Для beforebegin/afterend нужен допустимый родитель.
ВыборДля данных без разметки insertAdjacentText; для управляемых узлов insertAdjacentElement; insertAdjacentHTML разбирает разметку и требует отдельного контроля содержимого.
Четыре позиции
  1. beforebegin | <product> afterbegin Tea beforeend </product> | afterend
  2. Внутренние позиции меняют детей product
  3. Внешние позиции добавляют соседей у catalog
Добавить пояснение перед товаромСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$badge = $document->createElement("badge");
$product->insertAdjacentElement("beforebegin", $badge);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><badge/><product>Tea</product></catalog>

Элемент product остался прежним объектом; изменился его контекст.

Добавить содержимое внутри товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$detail = $document->createElement("detail");
$product->insertAdjacentElement("beforeend", $detail);
echo $document->saveXml($product), "
";
Ожидаемый результат
<product>Tea<detail/></product>

beforeend означает последнего ребёнка, не соседний элемент после product.

Связанные операции: DOMElement::insertAdjacentText.

DOMElement::insertAdjacentTextДобавить буквальный текст относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

PHP 8.3+ · dom

DOMElement::insertAdjacentText(string $where, string $data): void

Добавить буквальный текст относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

Аргумент / поведениеПолный контракт
$whereModern: enum Dom\AdjacentPosition. Legacy: строка beforebegin, afterbegin, beforeend, afterend (с PHP8.3). Первые/последние позиции вокруг элемента, средние — внутри него.
Второй аргументСтрока текста, не HTML-разметка; символы < и & остаются данными.</td>
Возвратvoid
ОшибкиНеверная legacy позиция → SYNTAX_ERR; в modern неверный тип позиции → TypeError. Нарушение дерева/чужой документ legacy → DOMException. Для beforebegin/afterend нужен допустимый родитель.
ВыборДля данных без разметки insertAdjacentText; для управляемых узлов insertAdjacentElement; insertAdjacentHTML разбирает разметку и требует отдельного контроля содержимого.
Четыре позиции
  1. beforebegin | <product> afterbegin Tea beforeend </product> | afterend
  2. Внутренние позиции меняют детей product
  3. Внешние позиции добавляют соседей у catalog
Добавить пояснение перед товаромСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$product->insertAdjacentText("beforebegin", "Sale: ");
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>Sale: <product>Tea</product></catalog>

Элемент product остался прежним объектом; изменился его контекст.

Добавить содержимое внутри товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$product->insertAdjacentText("beforeend", " & coffee");
echo $document->saveXml($product), "
";
Ожидаемый результат
<product>Tea &amp; coffee</product>

beforeend означает последнего ребёнка, не соседний элемент после product.

Связанные операции: DOMElement::insertAdjacentElement.

Dom\Element::insertAdjacentElementДобавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

PHP 8.4+ · dom

Dom\Element::insertAdjacentElement(Dom\AdjacentPosition $where, Dom\Element $element): ?Dom\Element

Добавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

Аргумент / поведениеПолный контракт
$whereModern: enum Dom\AdjacentPosition. Legacy: строка beforebegin, afterbegin, beforeend, afterend (с PHP8.3). Первые/последние позиции вокруг элемента, средние — внутри него.
Второй аргументElement того же семейства. Уже вставленный узел переносится, а не копируется; для копии cloneNode/importNode.
ВозвратВставленный Element либо null, если для внешней позиции нет подходящего родителя.
ОшибкиНеверная legacy позиция → SYNTAX_ERR; в modern неверный тип позиции → TypeError. Нарушение дерева/чужой документ legacy → DOMException. Для beforebegin/afterend нужен допустимый родитель.
ВыборДля данных без разметки insertAdjacentText; для управляемых узлов insertAdjacentElement; insertAdjacentHTML разбирает разметку и требует отдельного контроля содержимого.
Четыре позиции
  1. beforebegin | <product> afterbegin Tea beforeend </product> | afterend
  2. Внутренние позиции меняют детей product
  3. Внешние позиции добавляют соседей у catalog
Добавить пояснение перед товаромСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$badge = $document->createElement("badge");
$product->insertAdjacentElement(Dom\AdjacentPosition::BeforeBegin, $badge);
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog><badge/><product>Tea</product></catalog>

Элемент product остался прежним объектом; изменился его контекст.

Добавить содержимое внутри товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$detail = $document->createElement("detail");
$product->insertAdjacentElement(Dom\AdjacentPosition::BeforeEnd, $detail);
echo $document->saveXml($product), "
";
Ожидаемый результат
<product>Tea<detail/></product>

beforeend означает последнего ребёнка, не соседний элемент после product.

Связанные операции: Dom\Element::insertAdjacentText.

Dom\Element::insertAdjacentTextДобавить буквальный текст относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

PHP 8.4+ · dom

Dom\Element::insertAdjacentText(Dom\AdjacentPosition $where, string $data): void

Добавить буквальный текст относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.

Аргумент / поведениеПолный контракт
$whereModern: enum Dom\AdjacentPosition. Legacy: строка beforebegin, afterbegin, beforeend, afterend (с PHP8.3). Первые/последние позиции вокруг элемента, средние — внутри него.
Второй аргументСтрока текста, не HTML-разметка; символы < и & остаются данными.</td>
Возвратvoid
ОшибкиНеверная legacy позиция → SYNTAX_ERR; в modern неверный тип позиции → TypeError. Нарушение дерева/чужой документ legacy → DOMException. Для beforebegin/afterend нужен допустимый родитель.
ВыборДля данных без разметки insertAdjacentText; для управляемых узлов insertAdjacentElement; insertAdjacentHTML разбирает разметку и требует отдельного контроля содержимого.
Четыре позиции
  1. beforebegin | <product> afterbegin Tea beforeend </product> | afterend
  2. Внутренние позиции меняют детей product
  3. Внешние позиции добавляют соседей у catalog
Добавить пояснение перед товаромСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$product->insertAdjacentText(Dom\AdjacentPosition::BeforeBegin, "Sale: ");
echo $document->saveXml($document->documentElement), "
";
Ожидаемый результат
<catalog>Sale: <product>Tea</product></catalog>

Элемент product остался прежним объектом; изменился его контекст.

Добавить содержимое внутри товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
$product = $document->documentElement->firstChild;
$product->insertAdjacentText(Dom\AdjacentPosition::BeforeEnd, " & coffee");
echo $document->saveXml($product), "
";
Ожидаемый результат
<product>Tea &amp; coffee</product>

beforeend означает последнего ребёнка, не соседний элемент после product.

Связанные операции: Dom\Element::insertAdjacentElement.

Dom\Element::insertAdjacentHTMLВставить управляемую HTML-разметку компонента в современное дерево без ручного построения каждого тега.

PHP 8.4+ · dom

Dom\Element::insertAdjacentHTML(Dom\AdjacentPosition $where, string $string): void

Вставить управляемую HTML-разметку компонента в современное дерево без ручного построения каждого тега.

Аргумент / поведениеПолный контракт
$whereDom\AdjacentPosition: BeforeBegin, AfterBegin, BeforeEnd, AfterEnd.
$stringHTML для HTMLDocument, XML-фрагмент для XMLDocument. Разбирается в контексте элемента, включая его namespace/правила table/template.
Возврат / ошибкиvoid; неверный XML → SYNTAX_ERR; недопустимая внешняя позиция/структура → DOMException. Это не текстовый append.
БезопасностьНе вставляйте так произвольное сообщение пользователя: теги, события и опасные URL станут разметкой. Для текста insertAdjacentText; для разрешённого богатого HTML нужен отдельный санитайзер.
ВыполнениеPHP только меняет дерево. Если результат отправить браузеру, активная HTML-разметка уже может исполняться в браузере.
Строка становится узлами
  1. Sale — управляемый шаблон
  2. insertAdjacentHTML разбирает strong и Text
  3. saveHtml выдаёт реальный strong, не <strong>
Вставить готовую метку шаблонаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\HTMLDocument::createEmpty();
$article = $document->createElement("article");
$article->insertAdjacentHTML(Dom\AdjacentPosition::BeforeEnd, "<strong>Sale</strong>");
echo $document->saveHtml($article), "
";
Ожидаемый результат
<article><strong>Sale</strong></article>

Шаблон принадлежит приложению, внешних пользовательских тегов здесь нет.

В XML требуется корректная вложенностьСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product/>');
try {
    $document->documentElement->insertAdjacentHTML(Dom\AdjacentPosition::BeforeEnd, "<detail>");
} catch (DOMException $error) {
    echo "invalid XML fragment
";
}
Ожидаемый результат
invalid XML fragment

HTML-парсер умеет восстанавливать часть разметки; XML-контекст требует закрытых элементов.

Связанные операции: Dom\Element::insertAdjacentText.

Dom\Element::renameИзменить имя элемента при миграции XML-контракта, сохранив сам объект и его содержимое.

PHP 8.4+ · dom

Dom\Element::rename(?string $namespaceURI, string $qualifiedName): void

Изменить имя элемента при миграции XML-контракта, сохранив сам объект и его содержимое.

Аргумент / поведениеПолный контракт
$namespaceURI / $qualifiedNameНовый URI/null и согласованное qualifiedName. Меняются вместе, чтобы не возник промежуточный некорректный namespace.
Возвратvoid; изменён тот же объект. Для сохранения текущего URI передайте $node->namespaceURI.
ОшибкиINVALID_CHARACTER_ERR/NAMESPACE_ERR; INVALID_MODIFICATION_ERR при переносе в/из HTML namespace (он связан с классом HTMLElement) и переименовании template, хранящего отдельный fragment.
МиграцияПереименование не меняет ссылки XPath/CSS в вашем коде и не обновляет внешнюю XSD. Меняйте весь контракт согласованно.
Объект тот же, имя новое
  1. Исходное product Tea
  2. rename(null, "item")
  3. Тот же объект, новое имя, прежнее значение
Увидеть новое имя в изменённом XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="P1">Tea</product>');
$node = $document->documentElement;
$node->rename(null, "item");
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<item sku="P1">Tea</item>

rename меняет имя того же объекта. Значение атрибута или дочерний текст сохраняются. Файл, XPath и внешнюю XSD автоматически никто не переименовывает.

Добавить namespace версии форматаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="P1">Tea</product>');
$node = $document->documentElement;
$node->rename("urn:catalog:v2", "v2:item");
echo $node->namespaceURI, ":", $node->localName, "
";
Ожидаемый результат
urn:catalog:v2:item

URI задаёт смысл версии, префикс v2 — его короткое обозначение.

Связанные операции: Dom\Element::setAttributeNS, Dom\Document::createElementNS.

Dom\Attr::renameИзменить имя атрибута при миграции XML-контракта, сохранив сам объект и его содержимое.

PHP 8.4+ · dom

Dom\Attr::rename(?string $namespaceURI, string $qualifiedName): void

Изменить имя атрибута при миграции XML-контракта, сохранив сам объект и его содержимое.

Аргумент / поведениеПолный контракт
$namespaceURI / $qualifiedNameНовый URI/null и согласованное qualifiedName. Меняются вместе, чтобы не возник промежуточный некорректный namespace.
Возвратvoid; изменён тот же объект. Для сохранения текущего URI передайте $node->namespaceURI.
ОшибкиINVALID_CHARACTER_ERR/NAMESPACE_ERR для имени; INVALID_MODIFICATION_ERR при коллизии атрибутов одного расширенного имени.
МиграцияПереименование не меняет ссылки XPath/CSS в вашем коде и не обновляет внешнюю XSD. Меняйте весь контракт согласованно.
Объект тот же, имя новое
  1. Исходное sku=P1
  2. rename(null, "code")
  3. Тот же объект, новое имя, прежнее значение
Увидеть новое имя в изменённом XMLСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="P1">Tea</product>');
$node = $document->documentElement->getAttributeNode("sku");
$node->rename(null, "code");
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<product code="P1">Tea</product>

rename меняет имя того же объекта. Значение атрибута или дочерний текст сохраняются. Файл, XPath и внешнюю XSD автоматически никто не переименовывает.

Добавить namespace версии форматаСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="P1">Tea</product>');
$node = $document->documentElement->getAttributeNode("sku");
$node->rename("urn:catalog:v2", "v2:code");
echo $node->namespaceURI, ":", $node->localName, "
";
Ожидаемый результат
urn:catalog:v2:code

URI задаёт смысл версии, префикс v2 — его короткое обозначение.

Связанные операции: Dom\Element::setAttributeNS, Dom\Document::createElementNS.

DOMAttr::isIdУзнать, участвует ли атрибут в DOM-поиске по идентификатору.

PHP 5+ · dom

DOMAttr::isId(): bool

Узнать, участвует ли атрибут в DOM-поиске по идентификатору.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; bool: помечен ли этот Attr как ID. Имя поля само по себе не всегда определяет тип в legacy XML.
ОтличиеisId не проверяет уникальность значения и не сообщает, является ли оно ID записи базы. Для изменения роли используйте Element::setIdAttribute*.
Проверка роли поля
  1. sku=P1 существует как Attr
  2. setIdAttribute("sku", true) помечает ID
  3. isId() подтверждает роль; значение P1 не меняется
Использовать поле с ролью ID для поиска товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="P1">Tea</product>');
$product = $document->documentElement;
$product->setIdAttribute('sku', true);
$attribute = $product->getAttributeNode('sku');
if ($attribute->isId()) {
    echo 'Поиск P1: ', $document->getElementById('P1')->textContent, "\n";
}
Ожидаемый результат
Поиск P1: Tea

isId проверяет роль Attr в текущем документе. Это позволяет понять, почему getElementById найдёт данный товар. Уникальность P1 и права на товар проверяются отдельно.

Снять роль ID и сохранить значениеСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product sku="P1"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("sku");
$product->setIdAttribute("sku", true);
$product->setIdAttribute("sku", false);
var_dump($attribute->isId());
echo $attribute->value, "
";
Ожидаемый результат
bool(false)
P1

Имя и значение не исчезли вместе с пометкой.

Связанные операции: DOMElement::setIdAttribute.

Dom\Attr::isIdУзнать, участвует ли атрибут в DOM-поиске по идентификатору.

PHP 8.4+ · dom

Dom\Attr::isId(): bool

Узнать, участвует ли атрибут в DOM-поиске по идентификатору.

Аргумент / поведениеПолный контракт
Аргументы / возвратБез аргументов; bool: помечен ли этот Attr как ID. Имя поля само по себе не всегда определяет тип в legacy XML.
ОтличиеisId не проверяет уникальность значения и не сообщает, является ли оно ID записи базы. Для изменения роли используйте Element::setIdAttribute*.
Проверка роли поля
  1. sku=P1 существует как Attr
  2. setIdAttribute("sku", true) помечает ID
  3. isId() подтверждает роль; значение P1 не меняется
Использовать поле с ролью ID для поиска товараСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="P1">Tea</product>');
$product = $document->documentElement;
$product->setIdAttribute('sku', true);
$attribute = $product->getAttributeNode('sku');
if ($attribute->isId()) {
    echo 'Поиск P1: ', $document->getElementById('P1')->textContent, "\n";
}
Ожидаемый результат
Поиск P1: Tea

isId проверяет роль Attr в текущем документе. Это позволяет понять, почему getElementById найдёт данный товар. Уникальность P1 и права на товар проверяются отдельно.

Снять роль ID и сохранить значениеСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product sku="P1"/>');
$product = $document->documentElement;
$attribute = $product->getAttributeNode("sku");
$product->setIdAttribute("sku", true);
$product->setIdAttribute("sku", false);
var_dump($attribute->isId());
echo $attribute->value, "
";
Ожидаемый результат
bool(false)
P1

Имя и значение не исчезли вместе с пометкой.

Связанные операции: Dom\Element::setIdAttribute.

DOMDocumentFragment::appendXMLРазобрать несколько соседних XML-элементов и подготовить их к одной вставке.

PHP 5+ · dom

DOMDocumentFragment::appendXML(string $data): bool

Разобрать несколько соседних XML-элементов и подготовить их к одной вставке.

Аргумент / поведениеПолный контракт
$dataXML-фрагмент: допускает несколько корневых соседей, но каждый элемент должен быть корректно закрыт. Это расширение PHP DOM, не стандартный DOM-метод.
Возвратbool успеха. Невалидный фрагмент выдаёт libxml-диагностику и false; не принимайте частично созданное дерево за готовый импорт.
ВладелецЛучше создавайте fragment через Document::createDocumentFragment: известен document/контекст. Пространства имён в фрагменте задавайте явно.
ГраницаДля буквального текста используйте createTextNode. Для недоверенных файлов не включайте внешние сущности; XML-разбор не является HTML-санитайзером.
Разбор фрагмента
  1. <product>Tea</product><product>Coffee</product>
  2. appendXml → два ребёнка fragment
  3. appendChild(fragment) переносит оба в catalog
Вставить две позиции и показать каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$fragment = $document->createDocumentFragment();
if (!$fragment->appendXML('<product>Tea</product><product>Coffee</product>')) {
    throw new RuntimeException('Некорректные позиции');
}
$document->documentElement->appendChild($fragment);
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><product>Tea</product><product>Coffee</product></catalog>

Fragment разрешает подготовить соседние элементы без дополнительной обёртки. После вставки в catalog переместились оба ребёнка; самого тега fragment на выходе нет.

Отклонить обрыв перед вставкой в каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$fragment = $document->createDocumentFragment();
$previous = libxml_use_internal_errors(true);
try {
    if (!$fragment->appendXML("<product>")) { echo "Позиции не добавлены: закройте product\n"; }
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Позиции не добавлены: закройте product

При ошибке новый фрагмент не присоединяем. Нельзя продолжать вставку частичного результата только потому, что какие-то узлы успели создаться.

Связанные операции: DOMDocument::createDocumentFragment.

Dom\DocumentFragment::appendXmlРазобрать несколько соседних XML-элементов и подготовить их к одной вставке.

PHP 8.4+ · dom

Dom\DocumentFragment::appendXml(string $data): bool

Разобрать несколько соседних XML-элементов и подготовить их к одной вставке.

Аргумент / поведениеПолный контракт
$dataXML-фрагмент: допускает несколько корневых соседей, но каждый элемент должен быть корректно закрыт. Это расширение PHP DOM, не стандартный DOM-метод.
Возвратbool успеха. Невалидный фрагмент выдаёт libxml-диагностику и false; не принимайте частично созданное дерево за готовый импорт.
ВладелецЛучше создавайте fragment через Document::createDocumentFragment: известен document/контекст. Пространства имён в фрагменте задавайте явно.
ГраницаДля буквального текста используйте createTextNode. Для недоверенных файлов не включайте внешние сущности; XML-разбор не является HTML-санитайзером.
Разбор фрагмента
  1. <product>Tea</product><product>Coffee</product>
  2. appendXml → два ребёнка fragment
  3. appendChild(fragment) переносит оба в catalog
Вставить две позиции и показать каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$fragment = $document->createDocumentFragment();
if (!$fragment->appendXml('<product>Tea</product><product>Coffee</product>')) {
    throw new RuntimeException('Некорректные позиции');
}
$document->documentElement->appendChild($fragment);
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<catalog><product>Tea</product><product>Coffee</product></catalog>

Fragment разрешает подготовить соседние элементы без дополнительной обёртки. После вставки в catalog переместились оба ребёнка; самого тега fragment на выходе нет.

Отклонить обрыв перед вставкой в каталогСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<catalog/>');
$fragment = $document->createDocumentFragment();
$previous = libxml_use_internal_errors(true);
try {
    if (!$fragment->appendXml("<product>")) { echo "Позиции не добавлены: закройте product\n"; }
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Ожидаемый результат
Позиции не добавлены: закройте product

При ошибке новый фрагмент не присоединяем. Нельзя продолжать вставку частичного результата только потому, что какие-то узлы успели создаться.

Связанные операции: Dom\Document::createDocumentFragment.

DOMAttr::__constructСоздать отдельный старый DOMAttr и понять отличие свободного узла от узла, уже принадлежащего документу.

PHP 5+ · dom

DOMAttr::__construct(string $name, string $value = "")

Создать отдельный старый DOMAttr и понять отличие свободного узла от узла, уже принадлежащего документу.

Аргумент / поведениеПолный контракт
АргументыИмя и необязательное строковое значение атрибута; без namespace. INVALID_CHARACTER_ERR при недопустимом имени.
Результатnew возвращает объект; ownerDocument/ownerElement/parentNode ещё не обязательно установлены. Конструктор не записывает XML в файл.
Предпочтительный путьDocument::create* сразу связывает узел с document и уменьшает ошибки переноса между документами. Modern семейство использует эти фабрики вместо публичных конструкторов узлов.
Свободный узел
  1. new DOMAttr
  2. Свойства существуют, родителя ещё нет
  3. Фабрика document или явная вставка задаёт место в дереве
Посмотреть новый свободный узелСамостоятельный пример
<?php
declare(strict_types=1);

$node = new DOMAttr("stock", "4");
echo $node->name, ":", $node->value, "\n";
Ожидаемый результат
stock:4

Создан только объект, внешнего побочного действия нет.

Использовать созданный объект с документомСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = new DOMAttr("stock", "4");
$document->documentElement->setAttributeNode($node);
echo $document->documentElement->getAttribute("stock"), "\n";
Ожидаемый результат
4

Объекты одного семейства DOM работают совместно; для другого документа соблюдайте import/adopt.

Связанные операции: DOMDocument::createAttribute.

DOMElement::__constructСоздать отдельный старый DOMElement и понять отличие свободного узла от узла, уже принадлежащего документу.

PHP 5+ · dom

DOMElement::__construct(string $qualifiedName, ?string $value = null, string $namespace = "")

Создать отдельный старый DOMElement и понять отличие свободного узла от узла, уже принадлежащего документу.

Аргумент / поведениеПолный контракт
АргументыlocalName, необязательный value и необязательный namespace URI. Для произвольного текста безопаснее фабрика + createTextNode: строковый value имеет особенности XML-сущностей.
Результатnew возвращает объект; ownerDocument/ownerElement/parentNode ещё не обязательно установлены. Конструктор не записывает XML в файл.
Предпочтительный путьDocument::create* сразу связывает узел с document и уменьшает ошибки переноса между документами. Modern семейство использует эти фабрики вместо публичных конструкторов узлов.
Свободный узел
  1. new DOMElement
  2. Свойства существуют, родителя ещё нет
  3. Фабрика document или явная вставка задаёт место в дереве
Посмотреть новый свободный узелСамостоятельный пример
<?php
declare(strict_types=1);

$node = new DOMElement("product", "Tea");
echo $node->tagName, ":", $node->textContent, "\n";
Ожидаемый результат
product:Tea

Создан только объект, внешнего побочного действия нет.

Использовать созданный объект с документомСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = new DOMElement("product", "Tea");
$document->documentElement->appendChild($node);
echo $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
<catalog><product>Tea</product></catalog>

Объекты одного семейства DOM работают совместно; для другого документа соблюдайте import/adopt.

Связанные операции: DOMDocument::createElement.

DOMDocumentFragment::__constructСоздать отдельный старый DOMDocumentFragment и понять отличие свободного узла от узла, уже принадлежащего документу.

PHP 5+ · dom

DOMDocumentFragment::__construct()

Создать отдельный старый DOMDocumentFragment и понять отличие свободного узла от узла, уже принадлежащего документу.

Аргумент / поведениеПолный контракт
АргументыБез аргументов; создаётся пустой fragment без document. Свободный fragment в PHP8.5.11 не позволяет appendChild узла документа (NO_MODIFICATION_ALLOWED_ERR). Используйте Document::createDocumentFragment для рабочего контейнера.
Результатnew возвращает объект; ownerDocument/ownerElement/parentNode ещё не обязательно установлены. Конструктор не записывает XML в файл.
Предпочтительный путьDocument::create* сразу связывает узел с document и уменьшает ошибки переноса между документами. Modern семейство использует эти фабрики вместо публичных конструкторов узлов.
Свободный узел
  1. new DOMDocumentFragment
  2. Свойства существуют, родителя ещё нет
  3. Фабрика document или явная вставка задаёт место в дереве
Посмотреть новый свободный узелСамостоятельный пример
<?php
declare(strict_types=1);

$node = new DOMDocumentFragment();
echo $node->childNodes->length, "\n";
Ожидаемый результат
0

Создан только объект, внешнего побочного действия нет.

Использовать созданный объект с документомСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = new DOMDocumentFragment();
$node = $document->createDocumentFragment();
$node->appendChild($document->createElement("product"));
$document->documentElement->appendChild($node);
echo $document->saveXML($document->documentElement), "\n";
Ожидаемый результат
<catalog><product/></catalog>

Объекты одного семейства DOM работают совместно; для другого документа соблюдайте import/adopt.

Связанные операции: DOMDocument::createDocumentFragment.

DOMEntityReference::__constructСоздать отдельный старый DOMEntityReference и понять отличие свободного узла от узла, уже принадлежащего документу.

PHP 5+ · dom

DOMEntityReference::__construct(string $name)

Создать отдельный старый DOMEntityReference и понять отличие свободного узла от узла, уже принадлежащего документу.

Аргумент / поведениеПолный контракт
АргументыИмя сущности без & и ;, корректное XML-имя. Конструктор не объявляет сущность и не читает внешнюю DTD.
Результатnew возвращает объект; ownerDocument/ownerElement/parentNode ещё не обязательно установлены. Конструктор не записывает XML в файл.
Предпочтительный путьDocument::create* сразу связывает узел с document и уменьшает ошибки переноса между документами. Modern семейство использует эти фабрики вместо публичных конструкторов узлов.
Свободный узел
  1. new DOMEntityReference
  2. Свойства существуют, родителя ещё нет
  3. Фабрика document или явная вставка задаёт место в дереве
Посмотреть новый свободный узелСамостоятельный пример
<?php
declare(strict_types=1);

$node = new DOMEntityReference("tea");
echo $node->nodeName, "\n";
Ожидаемый результат
tea

Создан только объект, внешнего побочного действия нет.

Использовать созданный объект с документомСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<catalog/>');
$node = new DOMEntityReference("tea");
$document->documentElement->appendChild($node);
echo $document->saveXML($node), "\n";
Ожидаемый результат
&tea;

Объекты одного семейства DOM работают совместно; для другого документа соблюдайте import/adopt.

Связанные операции: DOMDocument::createEntityReference.

Dom\Node::__constructПонять, почему этот объект нельзя создавать вручную и откуда получить корректный экземпляр.

PHP 8.4+ · dom

Dom\Node::__construct()

Понять, почему этот объект нельзя создавать вручную и откуда получить корректный экземпляр.

Аргумент / поведениеПолный контракт
ДоступКонструктор закрыт для пользовательского вызова. new Dom\Node приводит к Error; это служебная граница DOM, не недостающий параметр.
НазначениеNode — базовый тип дерева; NamespaceInfo должен быть связан с namespace-контекстом элемента; TokenList должен быть связан с атрибутом class.
ПолучениеЧерез документную фабрику/свойство/метод соответствующего владельца. Полученный объект уже содержит необходимые внутренние связи.
Объект создаёт владелец
  1. Нельзя new Dom\Node в отрыве от DOM
  2. Документ/элемент знает нужное дерево и поле
  3. Фабрика возвращает настроенный объект
Увидеть ограничение ручного созданияСамостоятельный пример
<?php
declare(strict_types=1);

try {
    new Dom\Node();
} catch (Error $error) {
    echo "use DOM factory
";
}
Ожидаемый результат
use DOM factory

Перехвачена ошибка создания; приложение должно использовать поддерживаемый путь.

Получить связанный экземплярСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:p="urn:price" class="card"/>');
$value = $document->createElement("product");
echo get_class($value), "
";
Ожидаемый результат
Dom\Element

Объект появляется вместе с контекстом, необходимым для его работы.

Связанные операции: Dom\Document::createElement, Dom\Element::getInScopeNamespaces, Dom\TokenList::contains.

Dom\NamespaceInfo::__constructПонять, почему этот объект нельзя создавать вручную и откуда получить корректный экземпляр.

PHP 8.4+ · dom

Dom\NamespaceInfo::__construct()

Понять, почему этот объект нельзя создавать вручную и откуда получить корректный экземпляр.

Аргумент / поведениеПолный контракт
ДоступКонструктор закрыт для пользовательского вызова. new Dom\NamespaceInfo приводит к Error; это служебная граница DOM, не недостающий параметр.
НазначениеNode — базовый тип дерева; NamespaceInfo должен быть связан с namespace-контекстом элемента; TokenList должен быть связан с атрибутом class.
ПолучениеЧерез документную фабрику/свойство/метод соответствующего владельца. Полученный объект уже содержит необходимые внутренние связи.
Объект создаёт владелец
  1. Нельзя new Dom\NamespaceInfo в отрыве от DOM
  2. Документ/элемент знает нужное дерево и поле
  3. Фабрика возвращает настроенный объект
Увидеть ограничение ручного созданияСамостоятельный пример
<?php
declare(strict_types=1);

try {
    new Dom\NamespaceInfo();
} catch (Error $error) {
    echo "use DOM factory
";
}
Ожидаемый результат
use DOM factory

Перехвачена ошибка создания; приложение должно использовать поддерживаемый путь.

Получить связанный экземплярСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:p="urn:price" class="card"/>');
$value = $document->documentElement->getInScopeNamespaces()[0];
echo get_class($value), "
";
Ожидаемый результат
Dom\NamespaceInfo

Объект появляется вместе с контекстом, необходимым для его работы.

Связанные операции: Dom\Document::createElement, Dom\Element::getInScopeNamespaces, Dom\TokenList::contains.

Dom\TokenList::__constructПонять, почему этот объект нельзя создавать вручную и откуда получить корректный экземпляр.

PHP 8.4+ · dom

Dom\TokenList::__construct()

Понять, почему этот объект нельзя создавать вручную и откуда получить корректный экземпляр.

Аргумент / поведениеПолный контракт
ДоступКонструктор закрыт для пользовательского вызова. new Dom\TokenList приводит к Error; это служебная граница DOM, не недостающий параметр.
НазначениеNode — базовый тип дерева; NamespaceInfo должен быть связан с namespace-контекстом элемента; TokenList должен быть связан с атрибутом class.
ПолучениеЧерез документную фабрику/свойство/метод соответствующего владельца. Полученный объект уже содержит необходимые внутренние связи.
Объект создаёт владелец
  1. Нельзя new Dom\TokenList в отрыве от DOM
  2. Документ/элемент знает нужное дерево и поле
  3. Фабрика возвращает настроенный объект
Увидеть ограничение ручного созданияСамостоятельный пример
<?php
declare(strict_types=1);

try {
    new Dom\TokenList();
} catch (Error $error) {
    echo "use DOM factory
";
}
Ожидаемый результат
use DOM factory

Перехвачена ошибка создания; приложение должно использовать поддерживаемый путь.

Получить связанный экземплярСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product xmlns:p="urn:price" class="card"/>');
$value = $document->documentElement->classList;
echo get_class($value), "
";
Ожидаемый результат
Dom\TokenList

Объект появляется вместе с контекстом, необходимым для его работы.

Связанные операции: Dom\Document::createElement, Dom\Element::getInScopeNamespaces, Dom\TokenList::contains.

DOMNameSpaceNode::__sleepРаспознать запрет стандартной PHP-сериализации namespace-обёртки вместо сохранения неработоспособного объекта в сессии.

PHP 5+ · dom

DOMNameSpaceNode::__sleep(): array

Распознать запрет стандартной PHP-сериализации namespace-обёртки вместо сохранения неработоспособного объекта в сессии.

Аргумент / поведениеПолный контракт
Аргументы / поведениеБез аргументов; служебный magic method выбрасывает Exception в PHP8.5.11. Namespace-обёртка зависит от native дерева, serialize/unserialize не восстанавливают это владение.
АльтернативаСохраните необходимые строки prefix/namespaceURI и идентификатор контекста либо XML-документ. После загрузки заново получите namespace-представление.
Что сохранять
  1. DOMNameSpaceNode ссылается на живое дерево
  2. PHP serialize не поддерживает такую обёртку
  3. Сохраняем строковый URI/префикс либо исходный XML
Обработать неподдерживаемый magic methodСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price"/>');
$node = $document->documentElement->getAttributeNode("xmlns:p");
try {
    $node->__sleep();
} catch (Exception $error) {
    echo "DOM serialization unsupported
";
}
Ожидаемый результат
DOM serialization unsupported

Не пытайтесь вызывать magic methods как способ сохранить native DOM.

Сохранить нужный строковый URIСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price"/>');
$node = $document->documentElement->getAttributeNode("xmlns:p");
$data = ["uri" => $node->nodeValue];
echo $data["uri"], "
";
Ожидаемый результат
urn:price

Обычный массив строк не зависит от времени жизни libxml-узла.

Связанные операции: DOMNode::lookupNamespaceURI.

DOMNameSpaceNode::__wakeupРаспознать запрет стандартной PHP-сериализации namespace-обёртки вместо сохранения неработоспособного объекта в сессии.

PHP 5+ · dom

DOMNameSpaceNode::__wakeup(): void

Распознать запрет стандартной PHP-сериализации namespace-обёртки вместо сохранения неработоспособного объекта в сессии.

Аргумент / поведениеПолный контракт
Аргументы / поведениеБез аргументов; служебный magic method выбрасывает Exception в PHP8.5.11. Namespace-обёртка зависит от native дерева, serialize/unserialize не восстанавливают это владение.
АльтернативаСохраните необходимые строки prefix/namespaceURI и идентификатор контекста либо XML-документ. После загрузки заново получите namespace-представление.
Что сохранять
  1. DOMNameSpaceNode ссылается на живое дерево
  2. PHP serialize не поддерживает такую обёртку
  3. Сохраняем строковый URI/префикс либо исходный XML
Обработать неподдерживаемый magic methodСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price"/>');
$node = $document->documentElement->getAttributeNode("xmlns:p");
try {
    $node->__wakeup();
} catch (Exception $error) {
    echo "DOM serialization unsupported
";
}
Ожидаемый результат
DOM serialization unsupported

Не пытайтесь вызывать magic methods как способ сохранить native DOM.

Сохранить нужный строковый URIСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
$document->loadXML('<product xmlns:p="urn:price"/>');
$node = $document->documentElement->getAttributeNode("xmlns:p");
$data = ["uri" => $node->nodeValue];
echo $data["uri"], "
";
Ожидаемый результат
urn:price

Обычный массив строк не зависит от времени жизни libxml-узла.

Связанные операции: DOMNode::lookupNamespaceURI.

DOMImplementation::createDocumentTypeСоздать doctype для формата, которому нужен публичный/системный идентификатор DTD.

PHP 5+ · dom

DOMImplementation::createDocumentType(string $qualifiedName, string $publicId = "", string $systemId = ""): DOMDocumentType|false

Создать doctype для формата, которому нужен публичный/системный идентификатор DTD.

Аргумент / поведениеПолный контракт
$qualifiedNameИмя типа документа; корректное XML-qualifiedName. Некорректное имя/namespace → DOMException.
$publicId / $systemIdСтроки идентификаторов внешней DTD; в legacy необязательные пустые, modern требует передать оба явно. Это метаданные doctype, не команда немедленно скачать или проверить DTD.
ВозвратНовый DocumentType без ownerDocument; legacy также false при ошибке. Передаётся в createDocument.
ОграничениеМетод не добавляет объявления Entity/Notation, не подставляет сущности и значения атрибутов по умолчанию. Полная обработка DTD — отдельный шаг с контролем внешних ресурсов.
Подготовка типа документа
  1. createDocumentType("catalog", "", "catalog.dtd")
  2. Создан doctype с именем и системным идентификатором
  3. createDocument присоединяет его к новому документу
Создать тип без загрузки внешнего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$doctype = $implementation->createDocumentType("catalog", "", "catalog.dtd");
echo $doctype->name, ":", $doctype->systemId, "
";
Ожидаемый результат
catalog:catalog.dtd

В примере файл catalog.dtd не требуется: DTD не читается этим вызовом.

Связать подготовленный doctype с документомСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$doctype = $implementation->createDocumentType("catalog");
$document = $implementation->createDocument(null, "catalog", $doctype);
echo $document->doctype->name, ":", $document->documentElement->localName, "
";
Ожидаемый результат
catalog:catalog

Имена корня и doctype выбраны согласованно.

Связанные операции: DOMImplementation::createDocument.

DOMImplementation::createDocumentСоздать XML-документ сразу с корнем нужного пространства имён и необязательным doctype.

PHP 5+ · dom

DOMImplementation::createDocument(?string $namespace = null, string $qualifiedName = "", ?DOMDocumentType $doctype = null): DOMDocument

Создать XML-документ сразу с корнем нужного пространства имён и необязательным doctype.

Аргумент / поведениеПолный контракт
$namespace=null / $qualifiedName=""URI/null и имя корня; в modern оба аргумента обязательны, в legacy имеют значения по умолчанию. Пустое имя оставляет документ без корня. Если есть префикс, URI должен быть согласован.
$doctype=nullDocumentType того же семейства либо null. Уже использованный doctype нельзя бездумно присоединять к другому документу: WRONG_DOCUMENT_ERR/структурная ошибка.
ВозвратDOMDocument либо Dom\XMLDocument. Метода недостаточно для XSD/DTD-валидации и заполнения содержимого.
ОшибкиNAMESPACE_ERR/INVALID_CHARACTER_ERR для имени; WRONG_DOCUMENT_ERR/недопустимая структура doctype. Это нестатическая фабрика у Implementation.
Фабрика с корнем
  1. URI urn:catalog, имя c:catalog
  2. implementation.createDocument(...)
  3. Новый XML-документ уже содержит корневой элемент
Создать документ контрактного namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$document = $implementation->createDocument("urn:catalog", "c:catalog");
echo $document->documentElement->namespaceURI, ":", $document->documentElement->localName, "
";
Ожидаемый результат
urn:catalog:catalog

URI не загружается по сети; это идентификатор пространства имён.

Начать документ без корняСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$document = $implementation->createDocument();
var_dump($document->documentElement);
$document->appendChild($document->createElement("catalog"));
echo $document->documentElement->nodeName, "
";
Ожидаемый результат
NULL
catalog

Пустое имя даёт возможность выбрать корень позже.

Связанные операции: DOMImplementation::createDocumentType.

DOMImplementation::hasFeatureПрочитать историческое сообщение реализации о поддержке DOM-модуля, если сопровождается старый код.

PHP 5+ · dom

DOMImplementation::hasFeature(string $feature, string $version): bool

Прочитать историческое сообщение реализации о поддержке DOM-модуля, если сопровождается старый код.

Аргумент / поведениеПолный контракт
$feature / $versionНазвание модуля DOM, например XML/Core, и версия спецификации "1.0"/"2.0"; это не версия PHP.
Возвратbool. Этот старый механизм не подтверждает наличие конкретного modern PHP-метода или HTML5 parser.
Текущий выборДля функции PHP используйте function_exists, для класса class_exists, для метода method_exists и проверку требуемой версии. Не выбирайте HTML-санитайзер по hasFeature("HTML").
Исторический признак
  1. hasFeature("XML", "1.0")
  2. Старая таблица возможностей реализации
  3. Для современного API отдельно проверяем конкретный класс/метод
Спросить о базовом XML-модулеСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
var_dump($implementation->hasFeature("XML", "1.0"));
Ожидаемый результат
bool(true)

Ответ относится к историческому модулю DOM.

Проверить конкретный modern parserСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
echo class_exists(Dom\HTMLDocument::class) ? "HTML5 API available
" : "requires PHP 8.4+
";
Ожидаемый результат
HTML5 API available

Наличие современного класса — более точная проверка нужного вам интерфейса.

Связанные операции: DOMNode::isSupported, Dom\HTMLDocument::createFromString.

Dom\Implementation::createDocumentTypeСоздать doctype для формата, которому нужен публичный/системный идентификатор DTD.

PHP 8.4+ · dom

Dom\Implementation::createDocumentType(string $qualifiedName, string $publicId, string $systemId): Dom\DocumentType

Создать doctype для формата, которому нужен публичный/системный идентификатор DTD.

Аргумент / поведениеПолный контракт
$qualifiedNameИмя типа документа; корректное XML-qualifiedName. Некорректное имя/namespace → DOMException.
$publicId / $systemIdСтроки идентификаторов внешней DTD; в legacy необязательные пустые, modern требует передать оба явно. Это метаданные doctype, не команда немедленно скачать или проверить DTD.
ВозвратНовый DocumentType без ownerDocument; legacy также false при ошибке. Передаётся в createDocument.
ОграничениеМетод не добавляет объявления Entity/Notation, не подставляет сущности и значения атрибутов по умолчанию. Полная обработка DTD — отдельный шаг с контролем внешних ресурсов.
Подготовка типа документа
  1. createDocumentType("catalog", "", "catalog.dtd")
  2. Создан doctype с именем и системным идентификатором
  3. createDocument присоединяет его к новому документу
Создать тип без загрузки внешнего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$doctype = $implementation->createDocumentType("catalog", "", "catalog.dtd");
echo $doctype->name, ":", $doctype->systemId, "
";
Ожидаемый результат
catalog:catalog.dtd

В примере файл catalog.dtd не требуется: DTD не читается этим вызовом.

Связать подготовленный doctype с документомСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$doctype = $implementation->createDocumentType("catalog", "", "");
$document = $implementation->createDocument(null, "catalog", $doctype);
echo $document->doctype->name, ":", $document->documentElement->localName, "
";
Ожидаемый результат
catalog:catalog

Имена корня и doctype выбраны согласованно.

Связанные операции: Dom\Implementation::createDocument.

Dom\Implementation::createDocumentСоздать XML-документ сразу с корнем нужного пространства имён и необязательным doctype.

PHP 8.4+ · dom

Dom\Implementation::createDocument(?string $namespace, string $qualifiedName, ?Dom\DocumentType $doctype = null): Dom\XMLDocument

Создать XML-документ сразу с корнем нужного пространства имён и необязательным doctype.

Аргумент / поведениеПолный контракт
$namespace=null / $qualifiedName=""URI/null и имя корня; в modern оба аргумента обязательны, в legacy имеют значения по умолчанию. Пустое имя оставляет документ без корня. Если есть префикс, URI должен быть согласован.
$doctype=nullDocumentType того же семейства либо null. Уже использованный doctype нельзя бездумно присоединять к другому документу: WRONG_DOCUMENT_ERR/структурная ошибка.
ВозвратDOMDocument либо Dom\XMLDocument. Метода недостаточно для XSD/DTD-валидации и заполнения содержимого.
ОшибкиNAMESPACE_ERR/INVALID_CHARACTER_ERR для имени; WRONG_DOCUMENT_ERR/недопустимая структура doctype. Это нестатическая фабрика у Implementation.
Фабрика с корнем
  1. URI urn:catalog, имя c:catalog
  2. implementation.createDocument(...)
  3. Новый XML-документ уже содержит корневой элемент
Создать документ контрактного namespaceСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$document = $implementation->createDocument("urn:catalog", "c:catalog");
echo $document->documentElement->namespaceURI, ":", $document->documentElement->localName, "
";
Ожидаемый результат
urn:catalog:catalog

URI не загружается по сети; это идентификатор пространства имён.

Начать документ без корняСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$document = $implementation->createDocument(null, "");
var_dump($document->documentElement);
$document->appendChild($document->createElement("catalog"));
echo $document->documentElement->nodeName, "
";
Ожидаемый результат
NULL
catalog

Пустое имя даёт возможность выбрать корень позже.

Связанные операции: Dom\Implementation::createDocumentType.

Dom\Implementation::createHTMLDocumentПолучить готовый HTML-скелет с doctype, html, head и body для серверного отчёта.

PHP 8.4+ · dom

Dom\Implementation::createHTMLDocument(?string $title = null): Dom\HTMLDocument

Получить готовый HTML-скелет с doctype, html, head и body для серверного отчёта.

Аргумент / поведениеПолный контракт
$title=""Заголовок документа строкой. Он становится текстом title, а не HTML-кодом.
ВозвратDom\HTMLDocument с базовой структурой. В отличие от HTMLDocument::createEmpty, можно сразу обращаться к body/head.
ПрименениеДобавляйте контролируемые DOM-узлы в body, затем saveHtml. Создание документа не запускает браузер и не публикует страницу.
Готовый скелет
  1. createHTMLDocument("Catalog")
  2. doctype + html → head/title и body
  3. В body добавляется отчёт; saveHtml даёт страницу
Получить заголовок и телоСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$document = $implementation->createHTMLDocument("Catalog");
echo $document->title, ":", $document->body->localName, "
";
Ожидаемый результат
Catalog:body

head/body созданы фабрикой автоматически.

Добавить текст в тело отчётаСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = Dom\XMLDocument::createEmpty()->implementation;
$document = $implementation->createHTMLDocument("Catalog");
$paragraph = $document->createElement("p");
$paragraph->textContent = "Tea & coffee";
$document->body->appendChild($paragraph);
echo $document->saveHtml($paragraph), "
";
Ожидаемый результат
<p>Tea &amp; coffee</p>

Текст проходит через DOM, поэтому амперсанд сериализуется корректно.

Связанные операции: Dom\HTMLDocument::createEmpty, Dom\HTMLDocument::saveHtml.

DOMImplementation::__constructСоздать объект старых фабрик DOM для создания DocumentType и документа.

PHP 5+ · dom

DOMImplementation::__construct()

Создать объект старых фабрик DOM для создания DocumentType и документа.

Аргумент / поведениеПолный контракт
АргументыНет. Страница конструктора относится к PHP5/7; в актуальном PHP8 явного Reflection-метода __construct нет, однако new DOMImplementation() остаётся способом получить объект.
РезультатImplementation, а не DOMDocument. Для самого дерева затем createDocument.
ModernСовременный implementation получается у свойства document.implementation и имеет другой набор методов.
Два разных объекта
  1. new DOMImplementation
  2. Фабрика, умеющая создавать документы
  3. createDocument → отдельный DOMDocument
Создать фабрику и документСамостоятельный пример
<?php
declare(strict_types=1);

$implementation = new DOMImplementation();
$document = $implementation->createDocument(null, "catalog");
echo $document->documentElement->nodeName, "
";
Ожидаемый результат
catalog

Конструктор фабрики не создавал корень; его создал последующий метод.

Получить ту же возможность через документСамостоятельный пример
<?php
declare(strict_types=1);

$document = new DOMDocument();
echo get_class($document->implementation), "
";
Ожидаемый результат
DOMImplementation

Свойство implementation предоставляет доступ к поддерживаемым фабрикам.

Связанные операции: DOMImplementation::createDocument.

Dom\AdjacentPosition::casesВыбрать и проверить положение вставки узла без строковой опечатки внутри прикладного кода.

PHP 8.4+ · dom

static Dom\AdjacentPosition::cases(): array

Выбрать и проверить положение вставки узла без строковой опечатки внутри прикладного кода.

Аргумент / поведениеПолный контракт
ТипDom\AdjacentPosition — строковый backed enum. Четыре случая: BeforeBegin="beforebegin", AfterBegin="afterbegin", BeforeEnd="beforeend", AfterEnd="afterend".
Возвратcases(): array всех case в порядке объявления; from(value): enum либо ValueError при неизвестном значении; tryFrom(value): enum|null без ValueError для отсутствующего case.
Типы аргументовfrom/tryFrom принимают string|int по общему backed-enum API, но здесь значения строковые; передавайте строку ожидаемого формата. Неизвестная строка и неподходящий тип — разные ошибки.
ПользаСовременные insertAdjacent* требуют enum, не строку. Преобразуйте внешнюю настройку через tryFrom и задайте явную политику отсутствия; case.name — PHP-имя, case.value — строка позиции.
Настройка → enum → вставка
  1. "beforeend" из проверяемой настройки
  2. from/tryFrom → AdjacentPosition::BeforeEnd
  3. insertAdjacent* получает конкретный допустимый case
Получить допустимое положениеСамостоятельный пример
<?php
declare(strict_types=1);

foreach (Dom\AdjacentPosition::cases() as $position) {
    echo $position->value, "
";
}
Ожидаемый результат
beforebegin
afterbegin
beforeend
afterend

case.value используется для хранения/обмена, сам case — для вызова modern DOM.

Обработать конфигурацию позицийСамостоятельный пример
<?php
declare(strict_types=1);

$options = array_map(static fn (Dom\AdjacentPosition $position): string => $position->name, Dom\AdjacentPosition::cases());
echo count($options), ":", $options[0], "
";
Ожидаемый результат
4:BeforeBegin

Политика неверной настройки задана явно, а не скрыта внутри мутации дерева.

Связанные операции: Dom\Element::insertAdjacentElement.

Dom\AdjacentPosition::fromВыбрать и проверить положение вставки узла без строковой опечатки внутри прикладного кода.

PHP 8.4+ · dom

static Dom\AdjacentPosition::from(string|int $value): static

Выбрать и проверить положение вставки узла без строковой опечатки внутри прикладного кода.

Аргумент / поведениеПолный контракт
ТипDom\AdjacentPosition — строковый backed enum. Четыре случая: BeforeBegin="beforebegin", AfterBegin="afterbegin", BeforeEnd="beforeend", AfterEnd="afterend".
Возвратcases(): array всех case в порядке объявления; from(value): enum либо ValueError при неизвестном значении; tryFrom(value): enum|null без ValueError для отсутствующего case.
Типы аргументовfrom/tryFrom принимают string|int по общему backed-enum API, но здесь значения строковые; передавайте строку ожидаемого формата. Неизвестная строка и неподходящий тип — разные ошибки.
ПользаСовременные insertAdjacent* требуют enum, не строку. Преобразуйте внешнюю настройку через tryFrom и задайте явную политику отсутствия; case.name — PHP-имя, case.value — строка позиции.
Настройка → enum → вставка
  1. "beforeend" из проверяемой настройки
  2. from/tryFrom → AdjacentPosition::BeforeEnd
  3. insertAdjacent* получает конкретный допустимый case
Преобразовать настройку позиции и вставить меткуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product>Tea</product>');
$position = Dom\AdjacentPosition::from('beforeend');
$document->documentElement->insertAdjacentText($position, 'Sale: ');
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<product>TeaSale: </product>

Строка настройки превращается в допустимый enum. insertAdjacentText принимает этот case и вставляет буквальный текст в выбранную позицию. Для конца используйте текст с собственным начальным разделителем, например « — Sale».

Обработать конфигурацию позицийСамостоятельный пример
<?php
declare(strict_types=1);

try {
    Dom\AdjacentPosition::from("middle");
} catch (ValueError $error) {
    echo "unknown position
";
}
Ожидаемый результат
unknown position

Политика неверной настройки задана явно, а не скрыта внутри мутации дерева.

Связанные операции: Dom\Element::insertAdjacentElement.

Dom\AdjacentPosition::tryFromВыбрать и проверить положение вставки узла без строковой опечатки внутри прикладного кода.

PHP 8.4+ · dom

static Dom\AdjacentPosition::tryFrom(string|int $value): ?static

Выбрать и проверить положение вставки узла без строковой опечатки внутри прикладного кода.

Аргумент / поведениеПолный контракт
ТипDom\AdjacentPosition — строковый backed enum. Четыре случая: BeforeBegin="beforebegin", AfterBegin="afterbegin", BeforeEnd="beforeend", AfterEnd="afterend".
Возвратcases(): array всех case в порядке объявления; from(value): enum либо ValueError при неизвестном значении; tryFrom(value): enum|null без ValueError для отсутствующего case.
Типы аргументовfrom/tryFrom принимают string|int по общему backed-enum API, но здесь значения строковые; передавайте строку ожидаемого формата. Неизвестная строка и неподходящий тип — разные ошибки.
ПользаСовременные insertAdjacent* требуют enum, не строку. Преобразуйте внешнюю настройку через tryFrom и задайте явную политику отсутствия; case.name — PHP-имя, case.value — строка позиции.
Настройка → enum → вставка
  1. "beforeend" из проверяемой настройки
  2. from/tryFrom → AdjacentPosition::BeforeEnd
  3. insertAdjacent* получает конкретный допустимый case
Преобразовать настройку позиции и вставить меткуСамостоятельный пример
<?php
declare(strict_types=1);

$document = Dom\XMLDocument::createFromString('<product>Tea</product>');
$position = Dom\AdjacentPosition::tryFrom('afterbegin');
if ($position === null) { throw new RuntimeException("Неизвестная позиция"); }
$document->documentElement->insertAdjacentText($position, 'Sale: ');
echo $document->saveXml($document->documentElement), "\n";
Ожидаемый результат
<product>Sale: Tea</product>

Строка настройки превращается в допустимый enum. insertAdjacentText принимает этот case и вставляет буквальный текст в выбранную позицию. Для конца используйте текст с собственным начальным разделителем, например « — Sale».

Обработать конфигурацию позицийСамостоятельный пример
<?php
declare(strict_types=1);

$position = Dom\AdjacentPosition::tryFrom("middle");
echo $position === null ? "reject configuration
" : $position->value;
Ожидаемый результат
reject configuration

Политика неверной настройки задана явно, а не скрыта внутри мутации дерева.

Связанные операции: Dom\Element::insertAdjacentElement.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.