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

Дерево руководства
Глава 123 · От основ к полному API
DOM: полный справочник XML- и HTML-дерева, классов и методов
От первого XML-элемента до XPath, пространств имён, изменения дерева, HTML5, валидации и двух полных семейств DOM API.
DOM: открыть файл товара, прочитать поле, изменить и сохранить
Один товар: из XML-строки в дерево и обратно
В примере меняем описание товара, сохраняя атрибут stock. Документ, элемент, атрибут и текст — разные части одной структуры.
- 1XML поставщика↓
<product stock="4">Чай</product> — пока обычная строка байтов.
- 2Document → Element product↓
createFromString строит дерево. product имеет Attr stock=4 и дочерний Text Чай.
- 3Прочитать и изменить↓
getAttribute("stock") возвращает строку 4. textContent="Кофе" заменяет текст у того же product.
- 4XML для получателя
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
declare(strict_types=1);
$document = Dom\XMLDocument::createFromFile(__DIR__ . '/product.xml');
echo $document->documentElement->textContent, "\n";
Ожидаемый результат
Desk
__DIR__ обозначает папку текущего PHP-файла; точка соединяет её с именем product.xml. Файл входит в архив, его содержимое показано выше. Мы прочитали дерево, исходный файл пока не меняли.
- Document — всё содержимое product.xml
- documentElement — Element product
- getAttribute("stock") — строка "4"
- Text внутри product — слово Desk
- 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\XPath | PHP8.4+, современный интерфейс. Явный выбор XML/HTML parser, стандартные namespace-правила. |
| PHP8.5 дополнения | children, outerHTML, getElementsByClassName; запрещён clone коллекций. Все точные сигнатуры сверены с PHP8.5.11. |
| libxml2.11.9 стенда | Версия движка независима от PHP; отдельные новые флаги могут отсутствовать. Не объявляем любой PHP8.5 равным libxml2.13+. |
<?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>
<?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. |
| Обработать современный HTML | Dom\HTMLDocument::createFromString; PHP не выполняет браузерный JavaScript. |
| Копировать между документами | Dom\Document::importNode; для переноса того же узла Dom\Document::adoptNode. |
| Проверить формат поставщика | Dom\Document::schemaValidateSource плюс собственная проверка значений/прав. |
| Гигабайтный последовательный XML | XMLReader вместо полного дерева; для потокового вывода XMLWriter. |
| Лёгкое чтение небольшого XML | SimpleXML; мост к 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, которые возвращают любой тип узла. |
- label — элемент
- внутри label один Text с data="A😀Б"
- length=3, strlen(data)=7: два разных измерения
<?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 также имеет конструктор.
- Text("A") → Text("B") → Comment → Text("C")
- У первого Text wholeText="AB"
- Сами два узла остаются двумя; 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, которые возвращают любой тип узла. |
- label — элемент
- внутри label один Text с data="A😀Б"
- length=3, strlen(data)=7: два разных измерения
<?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 также имеет конструктор.
- Text("A") → Text("B") → Comment → Text("C")
- У первого Text wholeText="AB"
- Сами два узла остаются двумя; 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.
- Функция получает ChildNode
- Вызывает remove() у элемента или текстового узла
- Конкретный объект разрывает собственную связь с родителем
<?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.
- Функция получает ChildNode
- Вызывает remove() у элемента или текстового узла
- Конкретный объект разрывает собственную связь с родителем
<?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.
- Element catalog → позиции и текст
- DocumentFragment → временная группа детей
- Document → корень, допустимые PI/комментарии/doctype
<?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.
- Element catalog → позиции и текст
- DocumentFragment → временная группа детей
- Document → корень, допустимые PI/комментарии/doctype
<?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. |
- element.class = "card featured card"
- classList.value читает строку, count/item — набор
- add/remove/toggle изменяют атрибут у того же element
<?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 и другие типы; состав определяется источником списка. Полученный объект хранит доступ к узлам, а не копию их строковых данных.
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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 и другие типы; состав определяется источником списка. Полученный объект хранит доступ к узлам, а не копию их строковых данных.
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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. Полученный объект хранит доступ к узлам, а не копию их строковых данных.
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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.
<?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.
<?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: операции над числами используют его числовую модель, поэтому денежные вычисления лучше выполнять отдельно в целых минимальных единицах.
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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:product | product в 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: операции над числами используют его числовую модель, поэтому денежные вычисления лучше выполнять отдельно в целых минимальных единицах.
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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:product | product в 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.
- BeforeBegin | <product>
- AfterBegin → существующее содержимое → BeforeEnd
- </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.
- Документ хранит дерево XML/HTML
- Объект DOMException представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Исключение DOM сообщает, что действие нарушило структуру дерева. Например, XML-документ может иметь один корневой элемент. try/catch позволяет объяснить отказ и прекратить нужную операцию, сохранив действительный документ.
<?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 создался отдельно, но в документ не вставился.
| Свойство этого класса | Значение и границы |
|---|---|
| $code | DOMException несёт числовой код категории ошибки. Сопоставляйте с 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.
- Документ хранит дерево XML/HTML
- Объект DOMImplementation представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Все методы этого класса: DOMImplementation::hasFeature, DOMImplementation::createDocumentType, DOMImplementation::createDocument.
Официальное описание DOMImplementation; точные современные сигнатуры сверены с PHP 8.5.11.
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMImplementation::createDocument.
<?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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Implementation представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Все методы этого класса: 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.
- Документ хранит дерево XML/HTML
- Объект DOMNode представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Node представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
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.
- Документ хранит дерево XML/HTML
- Объект DOMNameSpaceNode представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMNameSpaceNode::__sleep.
<?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+.
- Документ хранит дерево XML/HTML
- Объект Dom\NamespaceInfo представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMDocumentFragment представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\DocumentFragment представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Document представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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: string | Modern отражение URI документа. Это метаданные и база разрешения ссылок, не сетевой HTTP-клиент. |
| $documentURI: string | Местоположение/база документа. Изменение влияет на разрешение относительных ссылок; не помещайте сюда непроверенный клиентский URL. |
| $characterSet: string | Modern каноническое имя кодировки документа. Входной overrideEncoding и кодировка сериализации имеют разные роли. |
| $charset: string | Modern синоним characterSet для совместимого DOM-интерфейса. |
| $inputEncoding: string | Modern синоним информации о кодировке документа; не определяет кодировку произвольной внешней PHP-строки. |
| $doctype: ?Dom\DocumentType | Только чтение. Объявление типа документа либо null. DTD и корень документа — разные узлы. |
| $documentElement: ?Dom\Element | Только чтение. Корневой элемент документа либо null у пустого. Это не сам Document. |
| $body: ?Dom\HTMLElement | HTML 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.
- Документ хранит дерево XML/HTML
- Объект DOMDocument представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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: bool | Legacy: по умолчанию true, структурные ошибки вызывают DOMException. false может дать предупреждение/false; дерево от этого не становится корректным. |
| $documentURI: ?string | Местоположение/база документа. Изменение влияет на разрешение относительных ссылок; не помещайте сюда непроверенный клиентский URL. |
| $config: mixed | Только чтение. Исторический незавершённый интерфейс DOMConfiguration, deprecated PHP8.4. Не используйте как объект работающих настроек normalizeDocument. |
| $formatOutput: bool | Отступы при сериализации XML. В legacy для загруженного дерева обычно нужно preserveWhiteSpace=false ДО load; сохранённые пробельные Text мешают ожидаемому форматированию. |
| $validateOnParse: bool | Legacy: загрузка и проверка DTD при разборе (по умолчанию false). Может затрагивать внешние ресурсы; не включайте для случайного XML клиента. |
| $resolveExternals: bool | Legacy: разрешение внешних сущностей/DTD. Не включайте для недоверенного XML; путь и сеть должны контролироваться отдельно. |
| $preserveWhiteSpace: bool | Legacy true по умолчанию сохраняет пробельные Text. false до загрузки соответствует LIBXML_NOBLANKS, но может менять смысл смешанного текста. |
| $recover: bool | Legacy попытка восстановить некорректный XML, специфичная libxml. Полученное дерево не доказывает, что вход полностью и правильно принят. |
| $substituteEntities: bool | Legacy подстановка сущностей, 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\HTMLDocument представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\XMLDocument представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMAttr представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Attr представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMElement представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: DOMElement::setAttribute.
<?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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Element представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: Dom\Element::setAttribute.
<?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: string | Modern: сериализованный HTML/XML внутри элемента; запись разбирает разметку в его контексте и заменяет детей. Для пользовательского текста textContent. Не HTML-санитайзер. |
| $outerHTML: string | Modern PHP8.5: сериализация самого элемента с детьми; запись заменяет его в родителе разобранной разметкой. Старая PHP-ссылка остаётся на отсоединённый старый объект. Недопустимая структура/Document-родитель может вызвать DOMException. |
| $substitutedNodeValue: string | Modern 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\HTMLElement представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMComment представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Comment представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMCdataSection представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\CDATASection представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMDocumentType представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\DocumentType представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMNotation представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Notation нужна старым XML-контрактам с не-XML вложениями. Сначала сущность cover сообщает имя нотации jpeg, затем карта notations даёт заявленный формат. Это метаданные, не загрузчик картинки.
<?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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Notation представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Notation нужна старым XML-контрактам с не-XML вложениями. Сначала сущность cover сообщает имя нотации jpeg, затем карта notations даёт заявленный формат. Это метаданные, не загрузчик картинки. В проверенной Windows-сборке PHP8.5.11 чтение systemId у Dom\Notation аварийно завершило процесс. Поэтому рабочий пример этой страницы извлекает метаданные через совместимый DOMDocument; это ограничение проверенной реализации, а не обещание одинакового поведения всех версий. Современный контракт ниже сохранён; try/catch не способен исправить нативное падение процесса.
<?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.
- Документ хранит дерево XML/HTML
- Объект DOMEntity представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
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+.
- Документ хранит дерево XML/HTML
- Объект Dom\Entity представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
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.
- Документ хранит дерево XML/HTML
- Объект DOMEntityReference представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\EntityReference представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Новых свойств по сравнению с указанным родителем/интерфейсом нет. Используйте унаследованный контракт; отсутствие новых полей не означает, что объекты не имеют состояния.
Унаследованные свойства и операции: 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.
- Документ хранит дерево XML/HTML
- Объект DOMProcessingInstruction представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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+.
- Документ хранит дерево XML/HTML
- Объект Dom\ProcessingInstruction представляет конкретную часть или операцию
- Методы и свойства ниже связывают эту часть с практическим действием
Посмотрите, какой конкретный результат даёт этот вид объекта. Подробный контракт операции: 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. Сравнивайте эти модели до того, как применять код к чужому форматированному файлу.
- catalog: Text перевода строки
- Затем product Tea, затем ещё Text
- 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><b>Tea</b></product>
Первое присваивание создаёт b; второе заменяет его буквальным текстом. Выбирайте по назначению данных.
Все константы DOM: типы узлов, ошибки и флаги
Тип nodeType — одно число; compareDocumentPosition — битовая маска; options парсера — другая маска; code исключения — категория ошибки. Нельзя произвольно объединять эти четыре назначения, даже если числовые значения совпадают. Константы доступны только с загруженным расширением.
| Константа | Назначение |
|---|---|
XML_ELEMENT_NODE = 1 | Element: узел-тег |
XML_ATTRIBUTE_NODE = 2 | Attr: поле элемента |
XML_TEXT_NODE = 3 | Text: обычный текст |
XML_CDATA_SECTION_NODE = 4 | CDATASection: текст с CDATA-сериализацией |
XML_ENTITY_REF_NODE = 5 | EntityReference: ссылка на объявленную сущность |
XML_ENTITY_NODE = 6 | Entity: объявление сущности |
XML_PI_NODE = 7 | ProcessingInstruction: инструкция получателю |
XML_COMMENT_NODE = 8 | Comment: комментарий |
XML_DOCUMENT_NODE = 9 | Document: весь XML-документ |
XML_DOCUMENT_TYPE_NODE = 10 | DocumentType: doctype |
XML_DOCUMENT_FRAG_NODE = 11 | DocumentFragment: временная группа узлов |
XML_NOTATION_NODE = 12 | Notation: DTD-нотация |
XML_HTML_DOCUMENT_NODE = 13 | HTML Document старого libxml |
XML_DTD_NODE = 14 | DTD-узел libxml |
XML_ELEMENT_DECL_NODE = 15 | DTD-объявление элемента |
XML_ATTRIBUTE_DECL_NODE = 16 | DTD-объявление атрибута |
XML_ENTITY_DECL_NODE = 17 | DTD-объявление сущности |
XML_NAMESPACE_DECL_NODE = 18 | Namespace-объявление legacy libxml |
XML_LOCAL_NAMESPACE = 18 | Namespace-объявление 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 = 4 | Legacy узел принадлежит другому документу: 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 = 10 | Attr уже используется другим 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 = 4 | Legacy узел принадлежит другому документу: 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 = 10 | Attr уже используется другим 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-переменной.
Ошибки входа, дерева и бизнес-правил: не скрывать частичный результат
- XML parser: можно ли вообще построить дерево?
- DOM операция: допустимы ли имя/родитель/документ?
- Приложение: можно ли принять этот заказ/остаток?
libxml_use_internal_errors собирает сообщения parser, но не перехватывает ValueError/TypeError/DOMException PHP. Сохраняйте прежний режим и очищайте буфер после операции. Ошибка в конце не должна оставлять половину обновлённого каталога видимой: сначала собирайте проверенный результат, затем фиксируйте его как цельную версию.
<?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.product с текстом Чай — 4 шт.
- XPath //p ищет без namespace: совпадения нет
- Префикс h связан с URI HTML: //h:p выбирает товар
- CSS p.product выбирает по тегу и метке class
- Изменение h1 → сохранённый catalog-report.html
<?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. Оба результата ссылаются на один реальный узел.
<?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: без глубокой копии потомки потерялись бы.
- A1 Desk stock=3 → копия в stock-report
- B2 Lamp stock=0 → пропускаем
- Строка bad вместо остатка → прекращаем попытку до записи
- Временный файл готов → переименовываем в stock-report.xml
ctype_digit принимает здесь только непустую строку цифр; ограничение длины 6 задаёт учебный диапазон до 999999. (int) преобразует проверенную строку для сравнения. throw прерывает попытку при ошибке данных. rename завершает подготовку файла в той же учебной папке; для заменяемого рабочего файла правила замены зависят от файловой системы и должны быть определены приложением.
<?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 и предметной проверкой.
<?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: 42292655 | empty объекта не проверяет число совпадений; отдельно false при отказе, затем length/item(null). DOMXPath::query |
| Stack Overflow: 1759137 | cloneNode сохраняет документ-владелец; импорт в целевой документ создаёт совместимую копию. DOMDocument::importNode |
| Stack Overflow: 8218230 | Старая проблема входной кодировки HTML; для PHP8.4 modern factory с осмысленным overrideEncoding. Старые советы HTML-ENTITIES не копируются как универсальное решение. Dom\HTMLDocument::createFromString |
| Stack Overflow: 79443721 | 2025: HTML namespace modern DOM требует XPath-префикса; сохраняем стандартный namespace, не отключаем его без причины. Dom\XPath::registerNamespace |
| Stack Overflow: 79374666 | 2025: querySelector относится к современному Dom, а не старому DOMDocument. Объясняем выбор семейства. Dom\Document::querySelector |
Полный указатель DOM: классы и все методы
Включены оба семейства, интерфейсы, служебные конструкторы, generated enum methods и мосты SimpleXML. Некоторые modern методы ещё не имеют отдельной страницы PHP.net: здесь они всё равно разобраны по tagged API PHP8.5.11 и поведению реализации. Выберите класс по задаче, затем конкретную операцию.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
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; сходство имён не делает их взаимозаменяемыми. |
- createElement: отдельный item без родителя
- appendChild(item): связь order → item
- В документе появился ребёнок; переменная 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; отключение не превращает ошибку структуры в корректную операцию. |
- order: item-A → item-B
- insertBefore(notice, item-B)
- 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; отключение не превращает ошибку структуры в корректную операцию. |
- product → price 100
- replaceChild(newPrice, oldPrice)
- 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), проход с конца или заранее сохранённый список объектов. |
- order → item; переменная item держит объект
- order.removeChild(item)
- 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 = false | false копирует сам узел; true рекурсивно копирует потомков. Атрибуты элемента копируются даже при false. У копии нет parentNode. |
| Возврат | Новый Node или false при невозможности клонирования. Копия обычного узла принадлежит прежнему ownerDocument; это не импорт в другой документ. |
| Идентификаторы и ограничения | id и другие атрибуты тоже копируются: приложение должно выдать новый id, если он должен быть уникален. Копируются DOM-данные, а не произвольная история PHP-объекта. Для другого документа используйте importNode; прямой appendChild чужого узла даёт WRONG_DOCUMENT_ERR. |
- Исходник: item id=A → name=Book
- cloneNode(true): второй item id=A → отдельный name=Book
- Изменить 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. Пробел с текстом остаётся значимым содержимым. |
- label → Text("A") → Text("") → Text("B")
- label.normalize()
- 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; сходство имён не делает их взаимозаменяемыми. |
- createElement: отдельный item без родителя
- appendChild(item): связь order → item
- В документе появился ребёнок; переменная 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- order: item-A → item-B
- insertBefore(notice, item-B)
- 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- product → price 100
- replaceChild(newPrice, oldPrice)
- 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), проход с конца или заранее сохранённый список объектов. |
- order → item; переменная item держит объект
- order.removeChild(item)
- 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 = false | false копирует сам узел; true рекурсивно копирует потомков. Атрибуты элемента копируются даже при false. У копии нет parentNode. |
| Возврат | Новый Node. Копия обычного узла принадлежит прежнему ownerDocument; это не импорт в другой документ. |
| Идентификаторы и ограничения | id и другие атрибуты тоже копируются: приложение должно выдать новый id, если он должен быть уникален. Копируются DOM-данные, а не произвольная история PHP-объекта. Документ/поддерево из другого документа можно перенести через adoptNode или копировать через importNode. |
- Исходник: item id=A → name=Book
- cloneNode(true): второй item id=A → отдельный name=Book
- Изменить 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. Пробел с текстом остаётся значимым содержимым. |
- label → Text("A") → Text("") → Text("B")
- label.normalize()
- 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, исключение для пустоты не требуется. |
- name → Text("Book")
- hasChildNodes() = true
- 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. |
- filter enabled=""
- hasAttributes() = true
- 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. |
- catalog → item → price; рядом settings
- item.contains(price) = true
- 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. |
- item создан документом: ownerDocument → document
- item не вставлен: parentNode = null
- 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. |
- root.firstChild → item-A
- getElementsByTagName("item")[0] → item-A
- 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. |
- item-A → name=Book
- clone item-B → name=Book: equal true, same false
- 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. |
- root.compareDocumentPosition(child)
- other=child следует после root и находится в нём
- 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 в логе, который может содержать адреса и токены. |
- Исходный XML: item на строке 2
- Проверка price обнаружила пустое значение
- 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+. Путь может раскрывать структуру документа: выдавайте только нужную диагностику. |
- catalog → item[1], item[2]
- item[2].getNodePath()
- /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 регистрируйте с собственным префиксом. |
- root: xmlns:p=urn:prices
- вложенный item: xmlns:p=urn:promo
- 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; ниже обе ветки дают понятную подпись. Второй пример объясняет, почему отсутствие именованного префикса совместимо с существующим пространством по умолчанию.
| Аргумент / поведение | Полный контракт |
|---|---|
| $namespace | URI пространства имён строкой в старом API. |
| Возврат | ?string: подходящий префикс либо null, если префикса нет. Пространство по умолчанию может существовать без возвращаемого префикса. При нескольких привязках не делайте бизнес-логику зависимой от выбора конкретного варианта. |
| Граница | Не создаёт декларацию xmlns и не регистрирует префикс в XPath. Для нового имени используйте createElementNS/setAttributeNS; для запроса registerNamespace. |
- xmlns:p="urn:prices"
- lookupPrefix("urn:prices") → p
- xmlns="urn:catalog" не даёт обязательного именованного префикса
<?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 не наследует пространство своего элемента.
| Аргумент / поведение | Полный контракт |
|---|---|
| $namespace | URI для сравнения строкой. |
| Возврат | bool: совпадает ли аргумент с пространством имён по умолчанию в контексте узла. |
| Важное различие | Непрефиксные атрибуты XML не наследуют default namespace элемента. Проверка не отвечает, в каком пространстве находится произвольный атрибут, и не проверяет PHP namespace. |
- catalog xmlns="urn:catalog"
- item без префикса находится в urn:catalog
- атрибут 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 сравнивается как строка, без обращения к сети.
<?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 метода нет. |
- isSupported("XML","1.0"): старый набор DOM XML
- method_exists(...,"compareDocumentPosition"): конкретный PHP API
- Ни один из них не валидирует входной документ
<?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, исключение для пустоты не требуется. |
- name → Text("Book")
- hasChildNodes() = true
- 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. |
- catalog → item → price; рядом settings
- item.contains(price) = true
- 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. |
- item создан документом: ownerDocument → document
- item не вставлен: parentNode = null
- 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. |
- root.firstChild → item-A
- getElementsByTagName("item")[0] → item-A
- 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. |
- item-A → name=Book
- clone item-B → name=Book: equal true, same false
- 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. |
- root.compareDocumentPosition(child)
- other=child следует после root и находится в нём
- 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 в логе, который может содержать адреса и токены. |
- Исходный XML: item на строке 2
- Проверка price обнаружила пустое значение
- 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+. Путь может раскрывать структуру документа: выдавайте только нужную диагностику. |
- catalog → item[1], item[2]
- item[2].getNodePath()
- /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 регистрируйте с собственным префиксом. |
- root: xmlns:p=urn:prices
- вложенный item: xmlns:p=urn:promo
- 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; ниже обе ветки дают понятную подпись. Второй пример объясняет, почему отсутствие именованного префикса совместимо с существующим пространством по умолчанию.
| Аргумент / поведение | Полный контракт |
|---|---|
| $namespace | URI пространства имён или null в современном API. |
| Возврат | ?string: подходящий префикс либо null, если префикса нет. Пространство по умолчанию может существовать без возвращаемого префикса. При нескольких привязках не делайте бизнес-логику зависимой от выбора конкретного варианта. |
| Граница | Не создаёт декларацию xmlns и не регистрирует префикс в XPath. Для нового имени используйте createElementNS/setAttributeNS; для запроса registerNamespace. |
- xmlns:p="urn:prices"
- lookupPrefix("urn:prices") → p
- xmlns="urn:catalog" не даёт обязательного именованного префикса
<?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 не наследует пространство своего элемента.
| Аргумент / поведение | Полный контракт |
|---|---|
| $namespace | URI для сравнения; современный API также принимает null для отсутствующего default namespace. |
| Возврат | bool: совпадает ли аргумент с пространством имён по умолчанию в контексте узла. |
| Важное различие | Непрефиксные атрибуты XML не наследуют default namespace элемента. Проверка не отвечает, в каком пространстве находится произвольный атрибут, и не проверяет PHP namespace. |
- catalog xmlns="urn:catalog"
- item без префикса находится в urn:catalog
- атрибут 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 сравнивается как строка, без обращения к сети.
<?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 = false | false использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера. |
| $withComments = false | Включить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму. |
| $xpath = null | null канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["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; для подписываемого сообщения дополнительно проверяйте, что выбран именно ожидаемый непустой набор узлов. |
- <item z="2" a="1"/>
- C14N: согласованные правила атрибутов и пустых элементов
- <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 = false | false использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера. |
| $withComments = false | Включить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму. |
| $xpath = null | null канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["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-дерева уже занята: запись в файл не превращает разбор в потоковый. |
- DOM заказа в памяти
- C14NFile(контролируемый путь, параметры)
- Файл содержит канонические байты; 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-сообщений. |
- PHP-переменная → DOM-объект → внутреннее дерево libxml
- serialize(node) вызывает защиту → исключение
- Сохранить XML-строку; при чтении создать новое DOM-дерево
<?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.
<?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; параметры, способные загрузить внешние сущности, требуют отдельного обоснования. |
- В кэше: строка <order id="A"/>
- Парсер XML создаёт новое дерево
- Новое дерево имеет данные заказа; это не прежняя PHP-идентичность
<?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 = false | false использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера. |
| $withComments = false | Включить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму. |
| $xpath = null | null канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["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; для подписываемого сообщения дополнительно проверяйте, что выбран именно ожидаемый непустой набор узлов. |
- <item z="2" a="1"/>
- C14N: согласованные правила атрибутов и пустых элементов
- <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 = false | false использует включающую каноникализацию, true — exclusive C14N: учитываются правила видимых namespace-префиксов. Это выбор алгоритма каноникализации, а не режим XML-парсера. |
| $withComments = false | Включить комментарии в результат при true. Этот выбор меняет байты и, следовательно, любую вычисленную по ним подпись/сумму. |
| $xpath = null | null канонизирует выбранный узел/документ обычным образом. Для фильтра передаётся ОДИН массив ["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-дерева уже занята: запись в файл не превращает разбор в потоковый. |
- DOM заказа в памяти
- C14NFile(контролируемый путь, параметры)
- Файл содержит канонические байты; 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-сообщений. |
- PHP-переменная → DOM-объект → внутреннее дерево libxml
- serialize(node) вызывает защиту → исключение
- Сохранить XML-строку; при чтении создать новое DOM-дерево
<?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.
<?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; параметры, способные загрузить внешние сущности, требуют отдельного обоснования. |
- В кэше: строка <order id="A"/>
- Парсер XML создаёт новое дерево
- Новое дерево имеет данные заказа; это не прежняя PHP-идентичность
<?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 меняет большее поддерево. |
- label → Text("Привет")
- text.appendData(", Анна")
- 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 & 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 может разделить составной символ. |
- Text("Привет"): П0 р1 и2 в3 е4 т5
- substringData(0, 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.
<?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. |
- Text("Заказ 42")
- insertData(6, "№")
- 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 с диагностикой. |
| Осторожность | Сначала проверьте, что нужный префикс действительно присутствует. Удалить первые семь символов у любого имени — испортить данные, а не очистить их. |
- Text("DRAFT: Book")
- Проверка starts_with("DRAFT: ")
- 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 с диагностикой. |
- Text("Price: 100")
- replaceData(7, 3, "1200")
- 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. |
- description → Text("Buy Book")
- splitText(4): Text("Buy ") + Text("Book")
- Создать 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.
<?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 меняет большее поддерево. |
- label → Text("Привет")
- text.appendData(", Анна")
- 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 & 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 может разделить составной символ. |
- Text("Привет"): П0 р1 и2 в3 е4 т5
- substringData(0, 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.
<?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. |
- Text("Заказ 42")
- insertData(6, "№")
- 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. |
| Осторожность | Сначала проверьте, что нужный префикс действительно присутствует. Удалить первые семь символов у любого имени — испортить данные, а не очистить их. |
- Text("DRAFT: Book")
- Проверка starts_with("DRAFT: ")
- 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. |
- Text("Price: 100")
- replaceData(7, 3, "1200")
- 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. |
- description → Text("Buy Book")
- splitText(4): Text("Buy ") + Text("Book")
- Создать 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.
<?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 публичного конструктора нет: используйте фабрику документа. |
- new DOMText("A & B") → отдельный узел
- label.appendChild(text) → узел включён
- 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 & 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 & B <partners></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. |
- pretty XML создаёт Text("\n ") между item
- Проверка сообщает «только XML-пробелы»
- Приложение решает, значимы ли эти пробелы для данного формата
<?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 как содержимое. Решение ничего не удаляет: пробел между словами может быть важным. Во втором примере пропускаются только отступы при чтении известного списка товаров.
<?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. |
- pretty XML создаёт Text("\n ") между item
- Проверка сообщает «только XML-пробелы»
- Приложение решает, значимы ли эти пробелы для данного формата
<?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 как содержимое. Решение ничего не удаляет: пробел между словами может быть важным. Во втором примере пропускаются только отступы при чтении известного списка товаров.
<?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 и персональные данные. |
- catalog → Comment("export version 2") → item
- Сериализация выдаёт <!--export version 2-->
- Получатель видит заметку; это не скрытое серверное хранилище
<?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 правила другие. |
- Строка "A < B & C"</li>
- CDATA сохраняет текст без превращения <B> в элемент
- При чтении textContent возвращает ту же строку
<?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><b>Book</b></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. |
- PI: target xml-stylesheet, data href="catalog.css"
- XML-сериализация сохраняет инструкцию
- Решение читать 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; отключение не превращает ошибку структуры в корректную операцию. |
- Родитель содержит исходный элемента
- before("[", badge, "]")
- 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><note><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; отключение не превращает ошибку структуры в корректную операцию. |
- Родитель содержит исходный элемента
- after("[", badge, "]")
- 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><note></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; отключение не превращает ошибку структуры в корректную операцию. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- 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. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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; отключение не превращает ошибку структуры в корректную операцию. |
- Родитель содержит исходный текстового узла
- before("[", badge, "]")
- 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><note>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; отключение не превращает ошибку структуры в корректную операцию. |
- Родитель содержит исходный текстового узла
- after("[", badge, "]")
- 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<note></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; отключение не превращает ошибку структуры в корректную операцию. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- 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. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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; отключение не превращает ошибку структуры в корректную операцию. |
- Родитель содержит исходный элемента
- before("[", badge, "]")
- 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><note><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; отключение не превращает ошибку структуры в корректную операцию. |
- Родитель содержит исходный элемента
- after("[", badge, "]")
- 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><note></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; отключение не превращает ошибку структуры в корректную операцию. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- 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. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Родитель содержит исходный элемента
- before("[", badge, "]")
- 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><note><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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Родитель содержит исходный элемента
- after("[", badge, "]")
- 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><note></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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- 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. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Родитель содержит исходный текстового узла
- before("[", badge, "]")
- 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><note>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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Родитель содержит исходный текстового узла
- after("[", badge, "]")
- 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<note></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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- 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. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Родитель содержит исходный элемента
- before("[", badge, "]")
- 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><note><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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Родитель содержит исходный элемента
- after("[", badge, "]")
- 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><note></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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- 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. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Document → DocumentType(catalog) → Element(catalog)
- before(Comment): новый сосед на уровне документа
- Корень 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- Document → DocumentType(catalog) → Element(catalog)
- after(Comment): новый сосед на уровне документа
- Корень 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 принимает узел другого современного документа и автоматически усыновляет его; это не копия. |
- parent → oldNode
- oldNode.replaceWith(newNodes...)
- parent → новые соседи; oldNode.parentNode = null
<?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), как в примере. |
- node.parentNode → родитель
- node.remove() разрывает связь
- 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-выгрузку. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new></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-выгрузку. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new><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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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-выгрузку. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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-выгрузку. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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-выгрузку. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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/><new>
Угловые скобки в строке остались данными. Для настоящего элемента вызывайте 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-выгрузку. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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), "
";
Ожидаемый результат
<new><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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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-выгрузку. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new></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-выгрузку. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new><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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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 отклоняется. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new></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 отклоняется. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new><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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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 отклоняется. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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 отклоняется. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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 отклоняется. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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/><new>
Угловые скобки в строке остались данными. Для настоящего элемента вызывайте 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 отклоняется. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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), "
";
Ожидаемый результат
<new><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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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 отклоняется. |
- Контейнер → уже существующие дети
- append(новые узлы) добавляет в конец
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new></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 отклоняется. |
- Контейнер → уже существующие дети
- prepend(новые узлы) добавляет в начало
- Родитель прежний; последовательность аргументов не разворачивается
<?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><new><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+ в новом. Соберите и проверьте новые данные до мутации. Операция над деревом не является транзакцией внешней БД и не сохраняет файл автоматически. |
- container → old children
- replaceChildren(newA, newB)
- тот же 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 не делает данные разрешёнными к выдаче. |
- article class="card"
- classList.add("featured", "card")
- 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) явно выражает намерение. |
- class="card pending busy"
- remove("pending", "missing")
- 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 = null | null/отсутствие аргумента меняет наличие на противоположное. 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 при втором проходе снимет добавленную метку. |
- stock=0 → soldOut=true
- toggle("sold-out", true)
- Повторный вызов с 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 выражает именно миграцию уже существующей метки. |
- class="card waiting"
- replace("waiting", "pending")
- 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 проверяет границы токенов. |
- class="card-large featured"
- contains("card") → false
- contains("featured") → true
<?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. |
- class="card featured card"
- Набор: [0]=card, [1]=featured
- 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. |
- class="card featured card"
- Разбор: card, featured
- 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 без копирования. |
- getIterator → card, debug-one, debug-two
- iterator_to_array фиксирует строки
- Удаление 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 при этом не анализирует. |
- contains("card"): есть в class?
- supports("card"): есть нормативный набор токенов атрибута? У class его нет
- 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 читает текст потомков; сериализация сохраняет разметку. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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 ничего не говорит об этом количестве. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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");
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. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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 читает текст потомков; сериализация сохраняет разметку. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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 ничего не говорит об этом количестве. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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;
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. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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 читает текст потомков; сериализация сохраняет разметку. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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 ничего не говорит об этом количестве. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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");
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. |
- catalog → product Tea, product Coffee
- items хранит связь с узлами, не их текстовую копию
- 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-селектор и не массив всех совпадений. |
- items = два поля формы
- namedItem("email") сравнивает id/name
- Возвращается узел второго 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 только подготовлен на сервере; форма здесь не отправляется.
<?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. |
- map содержит именованные записи
- item(0) выбирает позицию
- 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. |
- map.getNamedItem("id")
- Объект с nodeName=id
- У 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 / $localName | URI пространства имён и локальное имя без префикса. null/пустой URI обозначает отсутствие пространства имён. Префикс p не равен URI urn:price. |
| Возврат | Узел либо null. DTD entities/notations не образуют обычную карту XML-атрибутов с пространствами имён: для них обычно нужен getNamedItem. |
| Типичная ошибка | Запрос с URI="p" ищет другое пространство. Атрибут без префикса не получает namespace элемента по умолчанию. |
- p:amount связан с xmlns:p="urn:price"
- getNamedItemNS("urn:price", "amount")
- Выбор не зависит от того, назван префикс p или price
<?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. |
- Именованные записи → карта
- count() возвращает количество
- Состав всё ещё нужно проверять по именам
<?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 из недоверенного источника не нужно разрешать загружать только ради просмотра имени атрибута. |
- foreach получает объект каждой записи
- Берём nodeName, а не сериализуем весь документ
- Сортируем имена для стабильного отчёта
<?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. |
- map содержит именованные записи
- item(0) выбирает позицию
- 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. |
- map.getNamedItem("id")
- Объект с nodeName=id
- У 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 / $localName | URI пространства имён и локальное имя без префикса. null/пустой URI обозначает отсутствие пространства имён. Префикс p не равен URI urn:price. |
| Возврат | Узел либо null. DTD entities/notations не образуют обычную карту XML-атрибутов с пространствами имён: для них обычно нужен getNamedItem. |
| Типичная ошибка | Запрос с URI="p" ищет другое пространство. Атрибут без префикса не получает namespace элемента по умолчанию. |
- p:amount связан с xmlns:p="urn:price"
- getNamedItemNS("urn:price", "amount")
- Выбор не зависит от того, назван префикс p или price
<?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. |
- Именованные записи → карта
- count() возвращает количество
- Состав всё ещё нужно проверять по именам
<?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 из недоверенного источника не нужно разрешать загружать только ради просмотра имени атрибута. |
- foreach получает объект каждой записи
- Берём nodeName, а не сериализуем весь документ
- Сортируем имена для стабильного отчёта
<?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. |
- map содержит именованные записи
- item(0) выбирает позицию
- 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. |
- map.getNamedItem("tea")
- Объект с nodeName=tea
- У 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 / $localName | URI пространства имён и локальное имя без префикса. null/пустой URI обозначает отсутствие пространства имён. Префикс p не равен URI urn:price. |
| Возврат | Узел либо null. DTD entities/notations не образуют обычную карту XML-атрибутов с пространствами имён: для них обычно нужен getNamedItem. |
| Типичная ошибка | Запрос с URI="p" ищет другое пространство. Атрибут без префикса не получает namespace элемента по умолчанию. |
- p:amount связан с xmlns:p="urn:price"
- getNamedItemNS("urn:price", "amount")
- Выбор не зависит от того, назван префикс p или price
<?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. |
- Именованные записи → карта
- count() возвращает количество
- Состав всё ещё нужно проверять по именам
<?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 из недоверенного источника не нужно разрешать загружать только ради просмотра имени атрибута. |
- foreach получает объект каждой записи
- Берём nodeName, а не сериализуем весь документ
- Сортируем имена для стабильного отчёта
<?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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 / localName | URI (null для отсутствующего пространства) и локальное имя без префикса. Не передавайте p:amount вместо amount. |
| Возврат | Строка значения; отсутствие → пустая строка. |
| Пространства имён | xmlns:p="urn:price" связывает префикс и URI. Другой документ может назвать тот же URI префиксом price; поиск по URI продолжит работать. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 / localName | URI пространства и локальное имя. Атрибут без префикса не наследует namespace элемента по умолчанию. |
| Возврат | bool наличия точного расширенного имени; не проверяет его значение и допустимость по схеме. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
| Порядок | Для сравнения отчётов сортируйте копию массива. Не выводите из позиции атрибута его смысл/важность. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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-поведения проверяйте по назначению. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/Attr; изменение меняет тот же product
<?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 превращает изменённый узел в строку; другие атрибуты остаются.
<?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 & coffee"/>
Передаём обычный &, сериализатор записывает &. Ручной htmlspecialchars перед setAttribute дал бы двойное экранирование.
Связанные операции: DOMElement::setAttributeNS.
DOMElement::setAttributeNSДобавить сведения другой подсистемы, не смешивая одноимённые поля XML-контрактов.
PHP 5+ · dom
DOMElement::setAttributeNS(?string $namespace, string $qualifiedName, string $value): voidДобавить сведения другой подсистемы, не смешивая одноимённые поля XML-контрактов.
| Аргумент / поведение | Полный контракт |
|---|---|
| namespace / qualifiedName / value | URI (или null), имя с согласованным префиксом и строка значения. localName amount отличается от qualifiedName p:amount. |
| Возврат | void в modern; void/false при ошибке в legacy с выключенной строгой обработкой. |
| Ошибки | INVALID_CHARACTER_ERR для имени; NAMESPACE_ERR для префикса без URI, неверных комбинаций xml/xmlns и зарезервированных URI. NUL и неверные типы тоже не являются допустимыми данными. |
| Сериализация | DOM создаёт/сохраняет необходимые объявления префикса при сериализации. Не собирайте xmlns вручную строковыми заменами. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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->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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/Attr; изменение меняет тот же product
<?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 / localName | URI и локальное имя. Используйте тот же URI, по которому поле читалось, а не его префикс. |
| Возврат | void; отсутствие подходящего атрибута не создаёт ошибочное пустое поле. |
| Отличие | Удаление объявления xmlns в legacy имеет особенности namespace-узлов. Здесь речь об обычном атрибуте в заданном URI. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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=null | null переключает наличие; true обеспечивает наличие пустого атрибута, false удаляет. Если атрибут уже есть и force=true, значение сохраняется. |
| Возврат | bool наличия ПОСЛЕ операции; false может означать успешное удаление, не ошибку. |
| Ошибки | Недопустимое имя → DOMException. В HTML имя нормализуется по правилам HTML; XML остаётся чувствительным к регистру. |
| Повторяемость | Для повторной генерации используйте force с реальным состоянием, а не слепое переключение. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/Attr; изменение меняет тот же product
<?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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 к товару, сохранив заменённое поле при необходимости.
| Аргумент / поведение | Полный контракт |
|---|---|
| $attr | Attr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму. |
| Возврат | Старый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом. |
| Владение | Modern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR). |
| Практика | Для обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 к товару, сохранив заменённое поле при необходимости.
| Аргумент / поведение | Полный контракт |
|---|---|
| $attr | Attr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму. |
| Возврат | Старый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом. |
| Владение | Modern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR). |
| Практика | Для обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 для защищённого изменения. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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.
| Аргумент / поведение | Полный контракт |
|---|---|
| Поле | Квалифицированное имя существующего атрибута |
| $isId | true помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku. |
| Возврат / ошибки | void; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. |
| Граница | ID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка. |
- product sku="SKU-1"
- setIdAttribute*(…, true)
- 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.
<?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 существующего атрибута |
| $isId | true помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku. |
| Возврат / ошибки | void; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. |
| Граница | ID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка. |
- product sku="SKU-1"
- setIdAttribute*(…, true)
- 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.
<?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-объект этого элемента |
| $isId | true помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku. |
| Возврат / ошибки | void; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. |
| Граница | ID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка. |
- product sku="SKU-1"
- setIdAttribute*(…, true)
- 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.
<?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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 / localName | URI (null для отсутствующего пространства) и локальное имя без префикса. Не передавайте p:amount вместо amount. |
| Возврат | Строка значения; отсутствие → null. |
| Пространства имён | xmlns:p="urn:price" связывает префикс и URI. Другой документ может назвать тот же URI префиксом price; поиск по URI продолжит работать. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 / localName | URI пространства и локальное имя. Атрибут без префикса не наследует namespace элемента по умолчанию. |
| Возврат | bool наличия точного расширенного имени; не проверяет его значение и допустимость по схеме. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
| Порядок | Для сравнения отчётов сортируйте копию массива. Не выводите из позиции атрибута его смысл/важность. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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-поведения проверяйте по назначению. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/Attr; изменение меняет тот же product
<?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 превращает изменённый узел в строку; другие атрибуты остаются.
<?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 & 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 / value | URI (или null), имя с согласованным префиксом и строка значения. localName amount отличается от qualifiedName p:amount. |
| Возврат | void в modern; void/false при ошибке в legacy с выключенной строгой обработкой. |
| Ошибки | INVALID_CHARACTER_ERR для имени; NAMESPACE_ERR для префикса без URI, неверных комбинаций xml/xmlns и зарезервированных URI. NUL и неверные типы тоже не являются допустимыми данными. |
| Сериализация | DOM создаёт/сохраняет необходимые объявления префикса при сериализации. Не собирайте xmlns вручную строковыми заменами. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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->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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/Attr; изменение меняет тот же product
<?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 / localName | URI и локальное имя. Используйте тот же URI, по которому поле читалось, а не его префикс. |
| Возврат | void; отсутствие подходящего атрибута не создаёт ошибочное пустое поле. |
| Отличие | Удаление объявления xmlns в legacy имеет особенности namespace-узлов. Здесь речь об обычном атрибуте в заданном URI. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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=null | null переключает наличие; true обеспечивает наличие пустого атрибута, false удаляет. Если атрибут уже есть и force=true, значение сохраняется. |
| Возврат | bool наличия ПОСЛЕ операции; false может означать успешное удаление, не ошибку. |
| Ошибки | Недопустимое имя → DOMException. В HTML имя нормализуется по правилам HTML; XML остаётся чувствительным к регистру. |
| Повторяемость | Для повторной генерации используйте force с реальным состоянием, а не слепое переключение. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/Attr; изменение меняет тот же product
<?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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 к товару, сохранив заменённое поле при необходимости.
| Аргумент / поведение | Полный контракт |
|---|---|
| $attr | Attr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму. |
| Возврат | Старый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом. |
| Владение | Modern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR). |
| Практика | Для обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 к товару, сохранив заменённое поле при необходимости.
| Аргумент / поведение | Полный контракт |
|---|---|
| $attr | Attr того же семейства DOM. NS-вариант учитывает URI+localName при замене. Современный setAttributeNode следует тому же namespace-aware механизму. |
| Возврат | Старый заменённый Attr либо null при добавлении; legacy также false при нестрогой ошибке. Возврат не является новым элементом. |
| Владение | Modern запрещает чужой ownerElement (INUSE_ATTRIBUTE_ERR); свободный атрибут другого документа автоматически adopts. Legacy требует тот же документ либо узел без документа (WRONG_DOCUMENT_ERR). |
| Практика | Для обычной пары имя/строка короче setAttribute(NS). Объект нужен при переносе/переиспользовании уже найденного Attr. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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 для защищённого изменения. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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.
| Аргумент / поведение | Полный контракт |
|---|---|
| Поле | Квалифицированное имя существующего атрибута |
| $isId | true помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku. |
| Возврат / ошибки | void; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. |
| Граница | ID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка. |
- product sku="SKU-1"
- setIdAttribute*(…, true)
- 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.
<?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 существующего атрибута |
| $isId | true помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku. |
| Возврат / ошибки | void; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. |
| Граница | ID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка. |
- product sku="SKU-1"
- setIdAttribute*(…, true)
- 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.
<?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-объект этого элемента |
| $isId | true помечает атрибут типом ID, false снимает пользовательскую пометку. Это не присвоение значения id и не переименование sku. |
| Возврат / ошибки | void; NOT_FOUND_ERR если атрибут не принадлежит элементу/не существует, NO_MODIFICATION_ALLOWED_ERR при защищённом изменении. |
| Граница | ID обязан быть уникальным в документе по правилам данных. DOM-пометка не заменяет проверку дублей, не является первичным ключом вашей базы и не сохраняется как отдельная XML-разметка. |
- product sku="SKU-1"
- setIdAttribute*(…, true)
- 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.
<?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. Пустое значение всё равно считается существующим атрибутом. |
- product содержит stock="4" и p:amount="1200"
- Имя выбирает поле; URI+localName отличает контракт
- Чтение даёт строку/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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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; его загрузка/валидация требует контролируемого источника. |
| Правила | Идентификаторы должны быть уникальны; неоднозначность дублей не следует использовать как контракт. Пустое/отсутствующее совпадение обрабатывайте явно. |
- product sku="SKU-1"
- sku явно помечено типом ID
- getElementById("SKU-1") → один объект
<?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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- Tea: card sale; Coffee: card
- getElementsByClassName("card sale")
- Выбрана только 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; его загрузка/валидация требует контролируемого источника. |
| Правила | Идентификаторы должны быть уникальны; неоднозначность дублей не следует использовать как контракт. Пустое/отсутствующее совпадение обрабатывайте явно. |
- product sku="SKU-1"
- sku явно помечено типом ID
- getElementById("SKU-1") → один объект
<?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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- scope → catalog → group → product Tea
- Поиск видит вложенность любой глубины, а не только прямых детей
- Получаем живую коллекцию подходящих 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. |
- Tea: card sale; Coffee: card
- getElementsByClassName("card sale")
- Выбрана только 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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. |
- product.sale: выбрать товар с CSS-меткой sale
- PHP выполняет поиск в дереве на сервере
- Результат — объекты узлов; 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-условию, не искать его повторно во всём документе.
| Аргумент / поведение | Полный контракт |
|---|---|
| $selectors | CSS-селектор/список селекторов; проверяется сам Element, не его дети. |
| Возврат | bool. Неверный синтаксис → DOMException SYNTAX_ERR, неподдержанное условие → ValueError. |
| Применение | Удобно в обходе смешанных узлов, когда нужно отличить sale-товар от других. Для нахождения предка — closest. |
- product = уже найденный Tea
- matches("product.sale")
- 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. Это поиск родителей, не братьев/детей. |
- Tea product не group
- Родитель group.department совпал
- Возвращён 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. Обход всех потомков может быть значительно дороже и выдаёт отдельные контексты. |
- catalog объявляет p → urn:price
- product наследует p и добавляет d → urn:delivery
- 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. Обход всех потомков может быть значительно дороже и выдаёт отдельные контексты. |
- catalog объявляет p → urn:price
- product наследует p и добавляет d → urn:delivery
- 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 для имени. Создание не вставляет узел в документ. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 & coffee</title>
Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.
Связанные операции: DOMDocument::createTextNode.
DOMDocument::createElementNSСоздать элемент конкретного XML-контракта с правильным пространством имён.
PHP 5+ · dom
DOMDocument::createElementNS(?string $namespace, string $qualifiedName, string $value = ""): DOMElement|falseСоздать элемент конкретного XML-контракта с правильным пространством имён.
| Аргумент / поведение | Полный контракт |
|---|---|
| namespace / qualifiedName | URI/null и имя вида p:product. Префикс требует URI; xml/xmlns имеют зарезервированные значения. |
| value | Modern принимает только namespace и qualifiedName; legacy имеет дополнительный value с теми же ограничениями экранирования, что createElement. |
| Возврат / ошибки | Element (legacy может false); INVALID_CHARACTER_ERR/NAMESPACE_ERR при неверном имени/связи префикса и URI. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 / qualifiedName | URI/null и имя с префиксом. NAMESPACE_ERR при противоречивой комбинации; INVALID_CHARACTER_ERR при неверном имени. |
| Возврат | Свободный Attr; legacy может false. В старом API для корректной привязки namespace документ должен иметь корневой элемент; наши примеры его создают заранее. |
| Значение | Задаётся через Attr.value и прикрепляется setAttributeNodeNS. xmlns не является произвольной пользовательской областью. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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-контексте. Для атрибутов используйте их отдельные операции. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 & 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>
<strong>Sale</strong>
Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.
Связанные операции: 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. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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-разметки отвечает весь процесс генерации. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 / data | XML-имя назначения и строка данных. Legacy data необязательна (пустая по умолчанию), modern требует её явно. target xml зарезервирован, data не должна содержать ?>. |
| Возврат / ошибки | ProcessingInstruction; некорректное имя/данные → INVALID_CHARACTER_ERR (legacy в отдельных случаях warning/false). |
| Граница | Создание xml-stylesheet не запускает XSLT в PHP. Обработку выполняет отдельный потребитель. Не принимайте произвольные пути/URL инструкции от клиента. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 не сохраняет старую ветку; выбирайте по требованию, не только по скорости. |
- source → product Tea
- target.importNode(product, true)
- 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 не сохраняет старую ветку; выбирайте по требованию, не только по скорости. |
- source → product Tea
- target.adoptNode(product)
- 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-узел. |
- CatalogElement extends DOMElement
- registerNodeClass связывает базу и наследника
- 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 для имени. Создание не вставляет узел в документ. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 & coffee</title>
Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.
Связанные операции: Dom\Document::createTextNode.
Dom\Document::createElementNSСоздать элемент конкретного XML-контракта с правильным пространством имён.
PHP 8.4+ · dom
Dom\Document::createElementNS(?string $namespace, string $qualifiedName): Dom\ElementСоздать элемент конкретного XML-контракта с правильным пространством имён.
| Аргумент / поведение | Полный контракт |
|---|---|
| namespace / qualifiedName | URI/null и имя вида p:product. Префикс требует URI; xml/xmlns имеют зарезервированные значения. |
| value | Modern принимает только namespace и qualifiedName; legacy имеет дополнительный value с теми же ограничениями экранирования, что createElement. |
| Возврат / ошибки | Element (legacy может false); INVALID_CHARACTER_ERR/NAMESPACE_ERR при неверном имени/связи префикса и URI. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 / qualifiedName | URI/null и имя с префиксом. NAMESPACE_ERR при противоречивой комбинации; INVALID_CHARACTER_ERR при неверном имени. |
| Возврат | Свободный Attr; legacy может false. В старом API для корректной привязки namespace документ должен иметь корневой элемент; наши примеры его создают заранее. |
| Значение | Задаётся через Attr.value и прикрепляется setAttributeNodeNS. xmlns не является произвольной пользовательской областью. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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-контексте. Для атрибутов используйте их отдельные операции. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 & 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>
<strong>Sale</strong>
Объект можно заполнить до вставки. Сериализация и исходное текстовое значение различаются.
Связанные операции: 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. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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-разметки отвечает весь процесс генерации. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 / data | XML-имя назначения и строка данных. Legacy data необязательна (пустая по умолчанию), modern требует её явно. target xml зарезервирован, data не должна содержать ?>. |
| Возврат / ошибки | ProcessingInstruction; некорректное имя/данные → INVALID_CHARACTER_ERR (legacy в отдельных случаях warning/false). |
| Граница | Создание xml-stylesheet не запускает XSLT в PHP. Обработку выполняет отдельный потребитель. Не принимайте произвольные пути/URL инструкции от клиента. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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. |
- document создаёт узел с ownerDocument=document
- Новый объект ещё не ребёнок catalog
- 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 не сохраняет старую ветку; выбирайте по требованию, не только по скорости. |
- source → product Tea
- target.importNode(product, true)
- 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 не сохраняет старую ветку; выбирайте по требованию, не только по скорости. |
- source → product Tea
- target.adoptNode(product)
- 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-узел. |
- CatalogElement extends Dom\Element
- registerNodeClass связывает базу и наследника
- 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 с начала; мост нужен для совместимости. |
- Старая библиотека → DOMElement product
- modernDocument.importLegacyNode(old, true)
- Dom\Element product → вставка в новый документ
<?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/бизнес-контракту. |
- Строка уже находится в PHP
- load* разбирает разметку и заполняет document
- Ищем узлы; получаем 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/бизнес-контракту. |
- Файл читается
- load* разбирает разметку и заполняет document
- Ищем узлы; получаем Tea; исходный файл сам не меняется
<?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-санитайзером. |
- Строка уже находится в PHP
- load* разбирает разметку и заполняет document
- Ищем узлы; получаем 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-санитайзером. |
- Файл читается
- load* разбирает разметку и заполняет document
- Ищем узлы; получаем Tea; исходный файл сам не меняется
<?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=0 | XML-флаги libxml; не разрешайте внешние сущности/DTD и не отключайте лимиты для недоверенного документа. Неподдержанные флаги → ValueError. |
| $overrideEncoding=null | Принудительная кодировка входных байтов; null оставляет определение парсеру. Неизвестное имя → ValueError. Это кодировка ВХОДА, не имя кодировки будущего HTTP-ответа. |
| Возврат | Новый final Dom\XMLDocument; текущий объект не требуется, это статическая фабрика через ::. |
| Ошибки | Невозможность прочитать/разобрать документ → Exception/диагностика libxml; неправильные аргументы → ValueError. Некорректный HTML часто восстанавливается по HTML-правилам, XML значительно строже. |
| Стоимость | DOM хранит полное дерево в памяти. Для большого XML с последовательной обработкой выбирайте XMLReader. HTML parser не выполняет JS, CSS, загрузку картинок. |
- Входные байты XML
- Статическая createFromString создаёт дерево
- Переменная document получает готовый объект, затем читаем узлы
<?php
declare(strict_types=1);
$document = Dom\XMLDocument::createFromString('<catalog><product>Tea</product></catalog>');
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea
Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.
<?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=0 | XML-флаги libxml; не разрешайте внешние сущности/DTD и не отключайте лимиты для недоверенного документа. Неподдержанные флаги → ValueError. |
| $overrideEncoding=null | Принудительная кодировка входных байтов; null оставляет определение парсеру. Неизвестное имя → ValueError. Это кодировка ВХОДА, не имя кодировки будущего HTTP-ответа. |
| Возврат | Новый final Dom\XMLDocument; текущий объект не требуется, это статическая фабрика через ::. |
| Ошибки | Невозможность прочитать/разобрать документ → Exception/диагностика libxml; неправильные аргументы → ValueError. Некорректный HTML часто восстанавливается по HTML-правилам, XML значительно строже. |
| Стоимость | DOM хранит полное дерево в памяти. Для большого XML с последовательной обработкой выбирайте XMLReader. HTML parser не выполняет JS, CSS, загрузку картинок. |
- Входные байты XML
- Статическая createFromFile создаёт дерево
- Переменная document получает готовый объект, затем читаем узлы
<?php
declare(strict_types=1);
$document = Dom\XMLDocument::createFromFile("catalog.xml");
echo $document->getElementsByTagName("product")->item(0)->textContent, "
";
Ожидаемый результат
Tea
Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.
<?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; для копии существующего документа — соответствующая фабрика парсинга. |
- createEmpty → documentElement=null
- createElement создаёт корень
- 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, загрузку картинок. |
- Входные байты HTML
- Статическая createFromString создаёт дерево
- Переменная 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
Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.
<?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, загрузку картинок. |
- Входные байты HTML
- Статическая createFromFile создаёт дерево
- Переменная document получает готовый объект, затем читаем узлы
<?php
declare(strict_types=1);
$document = Dom\HTMLDocument::createFromFile("catalog.html");
echo $document->getElementsByTagName("p")->item(0)->textContent, "
";
Ожидаемый результат
Tea
Фабрика создаёт отдельный документ, а не меняет ранее открытое дерево.
<?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; для копии существующего документа — соответствующая фабрика парсинга. |
- createEmpty → documentElement=null
- createElement создаёт корень
- 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. Не смешивайте классы двух семейств. |
- new DOMDocument("1.0", "UTF-8")
- Пустой документ-владелец
- Создание и вставка 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — строка
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — записанный файл и число байтов
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — строка
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — записанный файл и число байтов
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — строка
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — записанный файл и число байтов
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — строка
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — записанный файл и число байтов
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — строка
<?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 & 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 хранит структуру, а правила разрешённых тегов/атрибутов задаёт отдельная политика. |
| Публикация файла | Для целостной выдачи генерируйте временный файл в контролируемой папке, затем заменяйте рабочий файл подходящим для окружения способом. Успех записи не доказывает принятие файла внешним сервисом. |
- Узлы уже содержат проверенные данные
- save* выбирает XML либо HTML serializer
- Результат — записанный файл и число байтов
<?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 & 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. |
- catalog: Text Tea + Text Coffee
- normalizeDocument объединяет соседний текст
- Содержимое 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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 нужны предметные правила. |
- <stock>4</stock> → parser строит DOM
- Схема требует nonNegativeInteger
- 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. |
- DTD: catalog должен содержать product+
- catalog с product → true
- Пустой catalog → false
<?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:// и локальные пути. |
| DTD | libxml может автоматически разрешать сущности внутри подключаемого XML с DTD, что меняет ожидаемое поведение. Примеры используют свои части без DTD. |
| Альтернатива | Для произвольных входящих XML лучше импортировать явно выбранные узлы через importNode, контролируя источник и размер. |
- catalog содержит xi:include href=product.xml
- xinclude читает управляемый product.xml
- В дереве появился 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 лежит в каталоге примера. Здесь нет обращения к сторонней сети.
<?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. Не используйте сущности для обхода экранирования или загрузки внешних ресурсов. |
- DTD объявляет tea="Tea"
- createEntityReference("tea") создаёт ссылку
- В 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>&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. |
- DTD: catalog должен содержать product+
- catalog с product → true
- Пустой catalog → false
<?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:// и локальные пути. |
| DTD | libxml может автоматически разрешать сущности внутри подключаемого XML с DTD, что меняет ожидаемое поведение. Примеры используют свои части без DTD. |
| Альтернатива | Для произвольных входящих XML лучше импортировать явно выбранные узлы через importNode, контролируя источник и размер. |
- catalog содержит xi:include href=product.xml
- xinclude читает управляемый product.xml
- В дереве появился 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 лежит в каталоге примера. Здесь нет обращения к сторонней сети.
<?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. Не используйте сущности для обхода экранирования или загрузки внешних ресурсов. |
- DTD объявляет tea="Tea"
- createEntityReference("tea") создаёт ссылку
- В 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>&tea;</catalog>
Text сохраняет буквальные символы &tea;, не создаёт ссылку на сущность.
Связанные операции: Dom\Document::createTextNode.
DOMXPath::__constructПодготовить поиск по дереву каталога: выбрать товары, атрибуты или вычислить итог одним XPath-выражением.
PHP 5+ · dom
DOMXPath::__construct(DOMDocument $document, bool $registerNodeNS = true)Подготовить поиск по дереву каталога: выбрать товары, атрибуты или вычислить итог одним XPath-выражением.
| Аргумент / поведение | Полный контракт |
|---|---|
| $document | DOMDocument для legacy либо Dom\Document для modern. XPath связан с этим документом; нельзя подставлять узлы чужого документа как контекст. |
| $registerNodeNS=true | Автоматически регистрировать namespace-префиксы контекстного узла. Для стабильного контракта можно отключить и зарегистрировать собственные префиксы вручную. |
| Результат | new создаёт XPath-объект. readonly document возвращает документ, registerNodeNamespaces управляет автоподхватом префиксов. |
| Версия / стандарт | Это XPath 1.0 на libxml, не XPath 2/3. С PHP 8.4 клонирование XPath запрещено; создавайте отдельный объект для другого документа. |
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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, которые соответствуют пути и условиям отбора.
| Аргумент / поведение | Полный контракт |
|---|---|
| $expression | XPath 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 не бесплатен. Результат хранит ссылки, не глубокие копии. |
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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.
| Аргумент / поведение | Полный контракт |
|---|---|
| $expression | XPath 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 не бесплатен. Результат хранит ссылки, не глубокие копии. |
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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. |
- XML: xmlns="urn:catalog"
- XPath: registerNamespace("c", "urn:catalog")
- /c:catalog/c:product выбирает Tea
<?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.
<?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. |
- Искомая строка содержит одинарные и двойные кавычки
- quote формирует корректный литерал/concat
- 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
Здесь значение простое; тот же код работает с кавычками.
<?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=null | null разрешает все доступные 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. |
- registerPhpFunctions разрешает только strtoupper
- XPath вызывает php:functionString для текста Tea
- 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 с нужным окружением.
<?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 / $name | URI функции и локальное имя без префикса; некорректное имя → ValueError. Дополнительно зарегистрируйте префикс через registerNamespace. |
| $callable | Вызываемая PHP-функция/замыкание. Node-set приходит как array DOM-узлов, а не строка XML и не NodeList. |
| Возврат / ошибки | Регистрация возвращает void. Невызываемое значение → TypeError; ошибки callback проявятся во время XPath-вызова. У метода нет аргументов options/overrideEncoding: такие строки в старом manual — посторонняя ошибка документации. |
| Граница | Не отдавайте недоверенному пользователю произвольный XPath с доступом к callable. Функция должна иметь узкую задачу и предсказуемые побочные эффекты. |
- r:label(/catalog/product[1])
- PHP callback получает массив из одного Element Tea
- Возвращает Item: Tea; evaluate получает строку
<?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-выражением.
| Аргумент / поведение | Полный контракт |
|---|---|
| $document | DOMDocument для legacy либо Dom\Document для modern. XPath связан с этим документом; нельзя подставлять узлы чужого документа как контекст. |
| $registerNodeNS=true | Автоматически регистрировать namespace-префиксы контекстного узла. Для стабильного контракта можно отключить и зарегистрировать собственные префиксы вручную. |
| Результат | new создаёт XPath-объект. readonly document возвращает документ, registerNodeNamespaces управляет автоподхватом префиксов. |
| Версия / стандарт | Это XPath 1.0 на libxml, не XPath 2/3. С PHP 8.4 клонирование XPath запрещено; создавайте отдельный объект для другого документа. |
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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, которые соответствуют пути и условиям отбора.
| Аргумент / поведение | Полный контракт |
|---|---|
| $expression | XPath 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 не бесплатен. Результат хранит ссылки, не глубокие копии. |
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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.
| Аргумент / поведение | Полный контракт |
|---|---|
| $expression | XPath 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 не бесплатен. Результат хранит ссылки, не глубокие копии. |
- /catalog/product выбирает товары
- [@stock > 0] оставляет товар с положительным остатком
- 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. |
- XML: xmlns="urn:catalog"
- XPath: registerNamespace("c", "urn:catalog")
- /c:catalog/c:product выбирает Tea
<?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.
<?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. |
- Искомая строка содержит одинарные и двойные кавычки
- quote формирует корректный литерал/concat
- 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
Здесь значение простое; тот же код работает с кавычками.
<?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=null | null разрешает все доступные 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. |
- registerPhpFunctions разрешает только strtoupper
- XPath вызывает php:functionString для текста Tea
- 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 с нужным окружением.
<?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 / $name | URI функции и локальное имя без префикса; некорректное имя → ValueError. Дополнительно зарегистрируйте префикс через registerNamespace. |
| $callable | Вызываемая PHP-функция/замыкание. Node-set приходит как array DOM-узлов, а не строка XML и не NodeList. |
| Возврат / ошибки | Регистрация возвращает void. Невызываемое значение → TypeError; ошибки callback проявятся во время XPath-вызова. У метода нет аргументов options/overrideEncoding: такие строки в старом manual — посторонняя ошибка документации. |
| Граница | Не отдавайте недоверенному пользователю произвольный XPath с доступом к callable. Функция должна иметь узкую задачу и предсказуемые побочные эффекты. |
- r:label(/catalog/product[1])
- PHP callback получает массив из одного Element Tea
- Возвращает Item: Tea; evaluate получает строку
<?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 в зависимости от причины; заранее работайте с реальным выбранным элементом. |
- SimpleXML: product Tea
- import_simplexml → DOM Element
- DOM.textContent = Coffee → SimpleXML тоже читает Coffee
<?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-кодирования: оба объекта смотрят на одно дерево.
<?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 в зависимости от причины; заранее работайте с реальным выбранным элементом. |
- SimpleXML: product Tea
- import_simplexml → DOM Element
- DOM.textContent = Coffee → SimpleXML тоже читает Coffee
<?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-кодирования: оба объекта смотрят на одно дерево.
<?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Добавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.
| Аргумент / поведение | Полный контракт |
|---|---|
| $where | Modern: 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 разбирает разметку и требует отдельного контроля содержимого. |
- beforebegin | <product> afterbegin Tea beforeend </product> | afterend
- Внутренние позиции меняют детей product
- Внешние позиции добавляют соседей у 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Добавить буквальный текст относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.
| Аргумент / поведение | Полный контракт |
|---|---|
| $where | Modern: 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 разбирает разметку и требует отдельного контроля содержимого. |
- beforebegin | <product> afterbegin Tea beforeend </product> | afterend
- Внутренние позиции меняют детей product
- Внешние позиции добавляют соседей у 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 & coffee</product>
beforeend означает последнего ребёнка, не соседний элемент после product.
Связанные операции: DOMElement::insertAdjacentElement.
Dom\Element::insertAdjacentElementДобавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.
PHP 8.4+ · dom
Dom\Element::insertAdjacentElement(Dom\AdjacentPosition $where, Dom\Element $element): ?Dom\ElementДобавить готовый элемент относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.
| Аргумент / поведение | Полный контракт |
|---|---|
| $where | Modern: 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 разбирает разметку и требует отдельного контроля содержимого. |
- beforebegin | <product> afterbegin Tea beforeend </product> | afterend
- Внутренние позиции меняют детей product
- Внешние позиции добавляют соседей у 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Добавить буквальный текст относительно выбранной карточки/элемента, не вычисляя родителей и соседей вручную.
| Аргумент / поведение | Полный контракт |
|---|---|
| $where | Modern: 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 разбирает разметку и требует отдельного контроля содержимого. |
- beforebegin | <product> afterbegin Tea beforeend </product> | afterend
- Внутренние позиции меняют детей product
- Внешние позиции добавляют соседей у 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 & 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-разметку компонента в современное дерево без ручного построения каждого тега.
| Аргумент / поведение | Полный контракт |
|---|---|
| $where | Dom\AdjacentPosition: BeforeBegin, AfterBegin, BeforeEnd, AfterEnd. |
| $string | HTML для HTMLDocument, XML-фрагмент для XMLDocument. Разбирается в контексте элемента, включая его namespace/правила table/template. |
| Возврат / ошибки | void; неверный XML → SYNTAX_ERR; недопустимая внешняя позиция/структура → DOMException. Это не текстовый append. |
| Безопасность | Не вставляйте так произвольное сообщение пользователя: теги, события и опасные URL станут разметкой. Для текста insertAdjacentText; для разрешённого богатого HTML нужен отдельный санитайзер. |
| Выполнение | PHP только меняет дерево. Если результат отправить браузеру, активная HTML-разметка уже может исполняться в браузере. |
- Sale — управляемый шаблон
- insertAdjacentHTML разбирает strong и Text
- 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>
Шаблон принадлежит приложению, внешних пользовательских тегов здесь нет.
<?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. Меняйте весь контракт согласованно. |
- Исходное product Tea
- rename(null, "item")
- Тот же объект, новое имя, прежнее значение
<?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 автоматически никто не переименовывает.
<?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. Меняйте весь контракт согласованно. |
- Исходное sku=P1
- rename(null, "code")
- Тот же объект, новое имя, прежнее значение
<?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 автоматически никто не переименовывает.
<?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*. |
- sku=P1 существует как Attr
- setIdAttribute("sku", true) помечает ID
- isId() подтверждает роль; значение P1 не меняется
<?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 и права на товар проверяются отдельно.
<?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*. |
- sku=P1 существует как Attr
- setIdAttribute("sku", true) помечает ID
- isId() подтверждает роль; значение P1 не меняется
<?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 и права на товар проверяются отдельно.
<?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-элементов и подготовить их к одной вставке.
| Аргумент / поведение | Полный контракт |
|---|---|
| $data | XML-фрагмент: допускает несколько корневых соседей, но каждый элемент должен быть корректно закрыт. Это расширение PHP DOM, не стандартный DOM-метод. |
| Возврат | bool успеха. Невалидный фрагмент выдаёт libxml-диагностику и false; не принимайте частично созданное дерево за готовый импорт. |
| Владелец | Лучше создавайте fragment через Document::createDocumentFragment: известен document/контекст. Пространства имён в фрагменте задавайте явно. |
| Граница | Для буквального текста используйте createTextNode. Для недоверенных файлов не включайте внешние сущности; XML-разбор не является HTML-санитайзером. |
- <product>Tea</product><product>Coffee</product>
- appendXml → два ребёнка fragment
- 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-элементов и подготовить их к одной вставке.
| Аргумент / поведение | Полный контракт |
|---|---|
| $data | XML-фрагмент: допускает несколько корневых соседей, но каждый элемент должен быть корректно закрыт. Это расширение PHP DOM, не стандартный DOM-метод. |
| Возврат | bool успеха. Невалидный фрагмент выдаёт libxml-диагностику и false; не принимайте частично созданное дерево за готовый импорт. |
| Владелец | Лучше создавайте fragment через Document::createDocumentFragment: известен document/контекст. Пространства имён в фрагменте задавайте явно. |
| Граница | Для буквального текста используйте createTextNode. Для недоверенных файлов не включайте внешние сущности; XML-разбор не является HTML-санитайзером. |
- <product>Tea</product><product>Coffee</product>
- appendXml → два ребёнка fragment
- 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 семейство использует эти фабрики вместо публичных конструкторов узлов. |
- new DOMAttr
- Свойства существуют, родителя ещё нет
- Фабрика 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 семейство использует эти фабрики вместо публичных конструкторов узлов. |
- new DOMElement
- Свойства существуют, родителя ещё нет
- Фабрика 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 семейство использует эти фабрики вместо публичных конструкторов узлов. |
- new DOMDocumentFragment
- Свойства существуют, родителя ещё нет
- Фабрика 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 семейство использует эти фабрики вместо публичных конструкторов узлов. |
- new DOMEntityReference
- Свойства существуют, родителя ещё нет
- Фабрика 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. |
| Получение | Через документную фабрику/свойство/метод соответствующего владельца. Полученный объект уже содержит необходимые внутренние связи. |
- Нельзя new Dom\Node в отрыве от DOM
- Документ/элемент знает нужное дерево и поле
- Фабрика возвращает настроенный объект
<?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. |
| Получение | Через документную фабрику/свойство/метод соответствующего владельца. Полученный объект уже содержит необходимые внутренние связи. |
- Нельзя new Dom\NamespaceInfo в отрыве от DOM
- Документ/элемент знает нужное дерево и поле
- Фабрика возвращает настроенный объект
<?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. |
| Получение | Через документную фабрику/свойство/метод соответствующего владельца. Полученный объект уже содержит необходимые внутренние связи. |
- Нельзя new Dom\TokenList в отрыве от DOM
- Документ/элемент знает нужное дерево и поле
- Фабрика возвращает настроенный объект
<?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-представление. |
- DOMNameSpaceNode ссылается на живое дерево
- PHP serialize не поддерживает такую обёртку
- Сохраняем строковый URI/префикс либо исходный XML
<?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.
<?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-представление. |
- DOMNameSpaceNode ссылается на живое дерево
- PHP serialize не поддерживает такую обёртку
- Сохраняем строковый URI/префикс либо исходный XML
<?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.
<?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 — отдельный шаг с контролем внешних ресурсов. |
- createDocumentType("catalog", "", "catalog.dtd")
- Создан doctype с именем и системным идентификатором
- 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 не читается этим вызовом.
<?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=null | DocumentType того же семейства либо null. Уже использованный doctype нельзя бездумно присоединять к другому документу: WRONG_DOCUMENT_ERR/структурная ошибка. |
| Возврат | DOMDocument либо Dom\XMLDocument. Метода недостаточно для XSD/DTD-валидации и заполнения содержимого. |
| Ошибки | NAMESPACE_ERR/INVALID_CHARACTER_ERR для имени; WRONG_DOCUMENT_ERR/недопустимая структура doctype. Это нестатическая фабрика у Implementation. |
- URI urn:catalog, имя c:catalog
- implementation.createDocument(...)
- Новый XML-документ уже содержит корневой элемент
<?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"). |
- hasFeature("XML", "1.0")
- Старая таблица возможностей реализации
- Для современного API отдельно проверяем конкретный класс/метод
<?php
declare(strict_types=1);
$implementation = new DOMImplementation();
var_dump($implementation->hasFeature("XML", "1.0"));
Ожидаемый результат
bool(true)
Ответ относится к историческому модулю DOM.
<?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 — отдельный шаг с контролем внешних ресурсов. |
- createDocumentType("catalog", "", "catalog.dtd")
- Создан doctype с именем и системным идентификатором
- 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 не читается этим вызовом.
<?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=null | DocumentType того же семейства либо null. Уже использованный doctype нельзя бездумно присоединять к другому документу: WRONG_DOCUMENT_ERR/структурная ошибка. |
| Возврат | DOMDocument либо Dom\XMLDocument. Метода недостаточно для XSD/DTD-валидации и заполнения содержимого. |
| Ошибки | NAMESPACE_ERR/INVALID_CHARACTER_ERR для имени; WRONG_DOCUMENT_ERR/недопустимая структура doctype. Это нестатическая фабрика у Implementation. |
- URI urn:catalog, имя c:catalog
- implementation.createDocument(...)
- Новый XML-документ уже содержит корневой элемент
<?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. Создание документа не запускает браузер и не публикует страницу. |
- createHTMLDocument("Catalog")
- doctype + html → head/title и body
- В 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 & 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 и имеет другой набор методов. |
- new DOMImplementation
- Фабрика, умеющая создавать документы
- 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 — строка позиции. |
- "beforeend" из проверяемой настройки
- from/tryFrom → AdjacentPosition::BeforeEnd
- 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 — строка позиции. |
- "beforeend" из проверяемой настройки
- from/tryFrom → AdjacentPosition::BeforeEnd
- 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 — строка позиции. |
- "beforeend" из проверяемой настройки
- from/tryFrom → AdjacentPosition::BeforeEnd
- 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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


