Множество разных спецификаций вокруг XML в первую очередь направлены на то, чтобы упорядочить и привести к единому стандарту подходы к работе с данными в формате XML. На данный момент существуют XML + XLink + XSL + пространства имён + информационное множество + XML Linking + Модель XPointer + пространства имён XPointer + xptr() XPointer + XSLT + XPath + XSL FO + DOM + SAX + PI для связи с листом стилей + XML-схема + XQuery + Шифрование XML + Канонизация XML + XML-подпись + DOM уровня 2 + DOM уровня 3 (список взят из статьи "С днем рождения, XML!"). Введение: о спецификациях XML-технологий
Что такое DOM
Document Object Model (объектная модель документа). Объект в данном случае значит объект в программистском смысле — артефакт ООП и все прекрасное, за что мы его любим.
Взглянем на исходный код XML-документа:
<?xml version=1.0 encoding=windows-1251?> <root language=russian> <title>XML: спецификация и функции DOM в PHP</title>
<text>Множество разных спецификаций вокруг <acronym>XML</acronym> в первую очередь направлены на то, чтобы <b>упорядочить</b> и привести к единому стандарту подходы к работе с данными в формате <acronym>XML</acronym>. </text>
<date>2003-05-12</date>
<raw-code> <![CDATA[ <br> пример не well-formed разметки: <p>bla-bla</p> ]]> </raw-code>
<!-- дописать в статью живые примеры надо бы... --> </root>
Основа идеологии XML в том, что документ — это набор узлов древовидной структуры данных. Данный документ можно представить в виде следующего дерева:
-o- Документ
+-o- Элемент root
+-o- Атрибут language
+-o- Элемент title
+-o- Текстовый узел ("XML: спецификация...")
+-o- Элемент text
+-o- Текстовый узел ("Множество...")
+-o- Элемент acronym
+-o- Текстовый узел ("XML")
+-o- Текстовый узел (" в первую очередь...")
+-o- Элемент b
+-o- Текстовый узел ("упорядочить")
+-o- Текстовый узел ("и привести...")
+-o- Элемент acronym
+-o- Текстовый узел ("XML")
+-o- Текстовый узел (".")
+-o- Элемент date
+-o- Текстовый узел ("2003-05-12")
+-o- Элемент raw-code
+-o- Секция CDATA ("<br>...")
+-o- Комментарий ("дописать...")
Знаком "-o-" на схеме обозначены узлы. Справа от них текст означает тип узла. Для текстовых узлов, секции CDATA и комментария добавлено содержимое — ради удобства ориентирования. На самом деле, по-хорошему, переносы строк между элементами являются текстовыми узлами, и их тоже можно было бы внести в схему.
Итак, разбираем схему. Всё, что есть в документе — узлы, и сам документ — тоже узел. Это значит, что есть класс объектов "узел", а остальные классы ("документ", "элемент", "текстовый узел", "CDATA", "комментарий") — дочерние от него и наследуют его свойства и методы. Какие свойства и методы должны содержаться в каких классах — описывается в спецификации DOM.
Если посмотреть в документацию по модулю DOM XML (тоже мне показатель :)), видно, что у всех этих разных узлов есть много общего — 28 методов у класса DomNode, а вместе с дочерними классами методов 62. Как можно догадаться, методы и свойства класса DomNode присутствуют и в других классах.
На сайте phpPatterns() недавно (9.4.3) появилась статья "Грубая схема модуля DOM XML в PHP" Гарри Фьюекса. Тем, кто по-английски умеет, можно прочитать первоисточник, остальным даю своё огрубление грубой схемы.
В статье приводится иллюстрация взаимоотношений классов модуля DOM XML. Вот дерево классов в моём исполнеии:
o- DomNode
+-o- DomAttribute
+-o- DomCData
+-o- DomComment
+-o- DomDTD
+-o- DomText
+-o- DomDocument
+-o- DomDocumentType
+-o- DomElement
+-o- DomEntity
+-o- DomEntityReference
+-o- DomProcessingInstruction
Далее приводятся замечания, что модуль DOM XML пока что не полностью соответствует спецификации (а "левыми" функциями уже каждый успел попользоваться, теперь во многих приложениях надо выковыривать их и переписывать код), что ещё много утечек памяти модуля будет исправлено в версии 4.3.2 (которая ещё не выпущена и находится в стадии релиз-кандидата). Но это мелочи жизни. Кто давно пользуется DOM XML, тому не привыкать, а если вы только начали знакомство с ним, то начнете использовать в реальных задачах уже тогда, когда он станет стабильным и будет соответствовать спецификации. В общем, продолжаем знакомство с DOM и модулем.
Спецификация DOM описывает то, какие объекты должны присутствовать в приложениях, работающих с XML, какие методы должны быть у этих объектов и как они должны влиять на узлы документа. Поэтому в языке Java, Javascript и других системах, где уже есть поддержка DOM, XML-документы имеют одинаковый интерфейс, различающийся только названиями функций. Страшно предположить, что было бы, начни разработчики самостоятельно изобретать модель. Работа в PHP с документом
Поддержка кириллицы
Стандарт предусматривает работу с данными, перекодированными в UTF-8, поэтому все функции по вводу данных требуют, чтобы они были перекодированы, а на выходе выдают тоже UTF-8. Для перекодировки нужно пользоваться функцией iconv.
Измененная библиотека php_domxml с поддержкой русского языка доступна на сайте dan.phpclub.net. Она может создавать объект документа из файла или строки, в которых в открывающем теге стоит соответствующий атрибут:
<?xml version=1.0 encoding=windows-1251?> русский текст
Функция dump_mem в ней тоже выдаёт текст в кодировке windows 1251, и на этом удобства заканчиваются - остальные данные нужно вводить в документ, перекодируя в UTF-8. Создание документа
Объект документа можно создать из существующего файла или текстовой строки, либо абсолютно новый пустой документ.
Все эти функции при ошибке возвращают не объект, значение false, так что проверка результата операции достаточно простая.
По умолчанию при создании документа производится проверка его синтаксиса (well-form), но не допустимости (соответствие DTD-схеме или XML-схеме документа, validity). Чтобы проверять и на допустимость, нужно указать в функции создания документа (любая из трех приведенных выше) второй, недокументированный пока, параметр и в нём константу DOMXML_LOAD_VALIDATING:
В памяти PHP после того, как документ был создан, хранятся все объекты элементов документа. Но в переменные скрипта они без специального вызова не записываются.
Корневой элемент документа можно получить, обратившись к объекту документа при помощи метода document_element. Функция возвращает объект класса DomElement, который можно использовать как аргумент другой функции, либо записать в переменную:
<? $root = $dom1->document_element(); ?>
Аналогично можно получить любой узел из документа — при помощи методов объекта документа или объектов элементов.
<? // Массив дочерних элементов корневого $root_child = $root->child_nodes();
// первый и последний дочерние элементы $first_child = $root->first_child(); $last_child = $root->last_child();
print($first_child->node_name(). и .$root_child[0]->node_name(). - одно и то же ); print($last_child->node_name(). и .$root_child[sizeof($root_child)-1]->node_name(). - тоже совпадают );
// элемент, следующий за первым // previous_sibling работает точно так же $second_child = $first_child->next_sibling();
При разборе дочерних элементов важно следить за типами узлов, потому что переносы строк, которые ставятся для удобства чтения и редактирования, тоже становятся узлами документа и, соответственно, входят в массив дочерних элементов.
<? for ($i = 0; $i < sizeof($root_child); $i++) if ($root_child[$i]->node_type() == XML_ELEMENT_NODE) // Для иллюстрации здесь текст перекодируется, хотя для латиницы //это необязательно $root_child[$i]->set_attribute(makes-sence, iconv(windows-1251, UTF-8, maybe)); else print($i - элемент типа . $root_child[$i]->node_type());
Впрочем иногда вообще нельзя быть уверенным в том, что получен объект узла, а не false или null. Тогда, если вызвать метод объекта, можно получить прямо в результирующий документ строчку с warning-ом. Чтобы этого избежать, можно проверять тип элемента функцией get_class.
А неуверенным в результате можно быть, например, когда вы достаёте нужный элемент из документа при помощи выражений XPath. Чтобы получить нужный элемент, не имеет смысла, конечно же, перебирать все элементы документа в его поисках. специально для этого есть выражения XPath, использующиеся в XSLT для адресации к узлам преобразуемого документа (атрибуты select, match).
<? /* Создание контекста XPath. Аргумент функции - объект документа, в котором выражения XPath будут выполняться. */ $context = xpath_new_context($dom1);
/* Выполнение выражения и запись результата в переменную result */ $result = xpath_eval($context, /root/text/acronym);
var_dump($result);
/* Переменная $result - объект класса XPathObject, свойство nodeset - массив, содержащий объекты полученных элементов. */ for ($i = 0; $i < sizeof($result->nodeset); $i++) { $text = $result->nodeset[$i]->first_child(); print(iconv(UTF-8, windows-1251, $text->node_value()). ); }
/* Получение скалярного значения при помощи XPath (подсчёт числа всех элементов в документе кроме корневого) */ $result = xpath_eval($context, count(/root//*));
var_dump($result); print( {$result->value});
Важно помнить про пространства имён XML, которые могут использоваться в документах. Если вы хотите выполнять выражения в документах, котоыре содержат элементы из своих пространств имён (например, XSLT-документы), вам нужно объявить это проистранство имён. Иначе нельзя будет указывать имена вида "xsl:template" в выражении.
Адрес (URI) пространства имён в аргументе функции обязательно должен совпадать с тем, что указан в документе, иначе XPath-парсер будет считать, что с одним и тем же префиксом xsl зарегистрированы два разных пространства имён.