Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Котеров Д. В., Костарев А. Ф. - PHP 5. 2-е издание (В подлиннике) - 2008

.pdf
Скачиваний:
6114
Добавлен:
29.02.2016
Размер:
11.36 Mб
Скачать

804

Часть VI. XML в PHP 5

Данный сценарий создает XHTML-документ с заголовком "test" и телом "проверка".

Все объекты этого документа создаются методами, описанными в стандартах DOM1 и DOM2. Обратите внимание на то, что метод createDocument() вместе с узлом типа domDocument создает и два его базовых дочерних узла — корневой узел и узел типа domDocumentType.

Результат выполнения сценария показан в листинге 38.26.

Листинг 38.26. Результат выполнения сценария из листинга 38.25

<?xml version="1.0" encoding="KOI8-R"?> <!DOCTYPE html PUBLIC

"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"> <body>Проверка</body>

</html>

Данный документ является XHTML-документом. В то же время не все браузеры, существующие на настоящий момент, могут корректно отобразить полученный документ, поскольку он начинается с XML-заголовка <?xml...?>. Для получения кор-

ректного HTML-документа вместо метода saveXML() необходимо использовать метод saveHTML(), который мы подробно обсудим в следующей главе.

Кроме методов createDocumentType() и createDocument() объект класса domImplementation

включает метод hasFeature(), который указывает, поддерживает ли данная реализа-

ция (в нашем случае реализация DOM в PHP 5) указанную версию модуля стандарта DOM. На данном методе мы поподробнее остановимся в следующей главе.

Перенос узлов между документами

Если в DOM1 узлы документа не имели "национальности", не были обременены

никакими связями с исходным документом и могли перемещаться без всяких про-

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

ределенные действия для совмещения областей действия старого и нового документов. Эти функции выполняет метод importNode() класса domDocument. Формат вызова метода следующий.

domNode importNode(Node importedNode, boolean deep)

Метод импортирует узел importedNode другого документа в окружение текущего документа. Если параметр deep имеет значение true, то вместе с указанным узлом импортируются и все его дочерние узлы. В качестве своего значения метод возвращает

копию узла или дерева узла, принадлежащего текущему документу (свойство ownerDocument указывает на текущий документ). Сформированный узел может быть

вставлен в качестве дочернего в любой узел документа методами appendChild() или insertBefore().

В качестве примера рассмотрим сценарий, приведенный в листинге 38.27.

Глава 38. DOM2 — пространства имен

805

Листинг 38.27. Файл newmethods/importNode.php

<?php ## Пример использования метода importNode класса domDocument. require_once 'unicode.inc';

$html = XMLHead . '

<html xmlns="http://www.w3.org/1999/xhtml" ><head><title>HTML-страница</title></head ><body><form><input name="bookname"/></form></body ></html>';

$domhtml = new domDocument('1.0', Encoding); $domhtml->loadXML($html); $form=$domhtml->getElementsbyTagName('form')->item(0);

$bookxml = XMLHead . '

<books xmlns="http:..."><book/><book/></books>'; $book=new domDocument('1.0', Encoding); $book->loadXML($bookxml);

$bookform = $book->importNode($form, true); $book->documentElement->appendChild($bookform); $book->formatOutput = true;

echo $book->saveXML();

В сценарии узел form переносится из XHTML-документа в XML-документ. Если бы мы попытались просто перенести данный узел из одного документа в другой:

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

то получили бы сообщение об ошибке при выполнении сценария. Создание копии

узла, в среде нового документа:

$bookform=$book->importNode($form,true);

решает данную проблему.

Результат выполнения сценария приведен в листинге 38.28.

Листинг 38.28. Результат выполнения сценария из листинга 38.27

<?xml version="1.0" encoding="KOI8-R"?>

<books xmlns="http:...">

<book/>

<book/>

<form xmlns="http://www.w3.org/1999/xhtml">

<input name="bookname"/>

</form>

</books>

Как вы видите, элемент form в новом документе, как и его дочерний элемент input,

сохранил область имен исходного документа.

806

Часть VI. XML в PHP 5

Нормализация узлов документа

Часто при интенсивной корректировке документа возникает ситуация, когда в спи-

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

Во-первых, изменяется система адресации документа. Если вы сохраните документ методом saveXML(), а затем загрузите его снова методом loadXML(), то в новом документе последовательно расположенные текстовые узлы сольются в один узел. Таким образом, одна и та же информация будет иметь различное логическое расположение в документе.

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

Чтобы избежать этих проблем, в стандарте DOM2 в интерфейс Node (класс domNode PHP 5) введен метод normalize(). Данный метод "сливает" все последовательно расположенные текстовые узлы в один узел. Эта операция производится как с поддеревом дочерних узлов текущего узла, так и с поддеревом атрибутов узла типа domElement.

Обработка исключительных ситуаций в DOM

Во время выполнения операций с DOM-объектами возможно возникновение ситуаций, когда запрошенное действие не может быть выполнено. По умолчанию интерпретатор в этом случае выводит сообщение об ошибке и завершает выполнение скрипта.

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

в PHP 5, появилась возможность перехвата управления при возникновении таких ситуаций и доведения сценария до логического конца. Перехват управления произ-

водится с помощью операторов try и catch (листинг 38.29).

Листинг 38.29. Файл exceptions/exception1.php

<?php ## Перехват управления в случае возникновения ошибки. $dom = new domDocument();

$root = new domElement('root'); try {

// добавить аргументы к элементу

appendattrs($root, Array('id'=>1, 'name'=>'first')); } catch (domException $exception) {

// если возникла исключительная ситуация print_r($exception); // отобразить состояние ошибки if ($exception->code == DOM_NO_MODIFICATION_ALLOWED_ERR) {

// если элемент не имеет владельца $dom->appendChild($root); // добавить его к документу

// и установить значения по умолчанию appendattrs($root, Array('id'=>0, 'name'=>'default'));

Глава 38. DOM2 — пространства имен

807

}else { // другие ошибки обработать стандартно throw $exception;

}

}

echo $dom->saveXML(); /**

*Добавить указанные атрибуты к элементу.

*@param domElement $element — корректируемый элемент

* @param array $attr — ассоциативный массив значений атрибутов

*/

function appendattrs($element, $attrs) {

foreach ($attrs as $name => $value) { // для всех элементов массива // установить указанный атрибут

$element->setAttribute($name, $value);

}

}

Функция appendattrs() добавляет к элементу, заданному первым параметром, атри-

буты, указанные в виде массива вторым параметром. В данном сценарии мы намеренно передаем функции элемент, созданный конструктором new и не связанный с определенным документом. При попытке корректировки такого элемента возникает исключительная ситуация DOM_NO_MODIFICATION_ALLOWED_ERR (модификация запрещена), которая приводит к завершению сценария. В нашем примере вызов функции находится в области действия оператора try. В этом случае при возникновении исключительной ситуации управление передается операторам, следующим за оператором catch.

В переменную $exception записывается информация о возникшей ошибке. Данная

переменная является объектом класса domException и имеет следующие свойства (см. листинг 38.30):

message — текст сообщения об ошибке;

file — имя файла выполняемого PHP-скрипта;

line — номер строки в файле;

code — код ошибки;

trace — массив трассировки, хранящий стек вызывающих функций и передавае-

мые ими параметры.

Свойства message, file, line имеют тип protected и, следовательно, доступ к ним напрямую вне методов класса domException невозможен. Для получения значения

этих переменных необходимо использовать методы класса Exception, являющегося родительским классом класса domException.

string getMessage()

Метод возвращает текст сообщения об ошибке.

string getFile()

Метод возвращает имя файла, где обнаружена ошибка.

808

Часть VI. XML в PHP 5

int getLine()

Метод возвращает номер строки, где обнаружена ошибка.

mixed getTrace()

Метод возвращает трассировку места возникновения ошибки.

string getTraceAsString()

Метод возвращает трассировку места возникновения ошибки в текстовой форме.

string __toString()

Метод возвращает полную строку описания ошибки.

В приведенном сценарии (листинг 38.30) при возникновении исключительной ситуации проверяется код ошибки. Если он равен константе DOM_NO_MODIFICATION_ ALLOWED_ERR, то производится добавление элемента root, вызвавшего ошибку к доку-

менту $dom. После добавления элемент приобретает свойство ownerDocument и может быть модифицирован. В нашем случае повторный вызов функции appendattrs() до-

бавляет к элементу атрибуты id и name.

Если код ошибки другой, то вызывается оператор throw($exception), который передает управление оператору catch более высокого уровня. Так как в нашем случае

такого оператора нет, то вызывается стандартная реакция на ошибку — интерпретатор выводит текст ошибки, ее место и завершает выполнение скрипта.

Результат выполнения показан в листинге 38.30.

Листинг 38.30. Результат выполнения сценария exception1.php

domexception Object(

[message:protected] => No Modification Allowed Error [string:private] =>

[file:protected] => .../exception1.php [line:protected] => 24 [trace:private] =>

[code] => 7 [trace] => Array(

[0] => Array(

[file] => .../exception1.php [line] => 5

[function] => appendattrs [args] => Array(

[0]=> id

[1]=> 1

)

)

)

)

<?xml version="1.0"?>

<root id="0" name="default"/>

Глава 38. DOM2 — пространства имен

809

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

димые действия для их исправления.

Коды исключительных ситуаций стандартизированы спецификациями DOM. В листинге 38.31 представлен их список.

Листинг 38.31. Файл exceptions/rusexceptions.inc

<?php ## Функция перевода кодов исключительных ситуаций в текст. /**

*Перевести номер ошибки в описание на русском языке.

*@param int $code — номер ошибки

* $return — текст описания

*/

function domexeptionmessage($code) {

$rusmes = Array(

DOM_INDEX_SIZE_ERR =>

"Индекс отрицателен или больше допустимого значения",

DOMSTRING_SIZE_ERR =>

"Ошибка в размере строки типа DOMSTRING",

DOM_HIERARCHY_REQUEST_ERR =>

"Узел добавляется в место, к которому он не принадлежит",

DOM_WRONG_DOCUMENT_ERR =>

"Узел используется в документе, к которому он не принадлежит",

DOM_INVALID_CHARACTER_ERR =>

"В имени используется некорректный символ",

DOM_NO_DATA_ALLOWED_ERR =>

"К узлу добавляются данные, которые он не может содержать",

DOM_NO_MODIFICATION_ALLOWED_ERR =>

"Попытка модификации узла, который не может быть изменен",

DOM_NOT_FOUND_ERR =>

"Доступ к узлу в данном контексте невозможен",

DOM_NOT_SUPPORTED_ERR =>

"Реализация не поддерживает данный объект или операцию ",

DOM_INUSE_ATTRIBUTE_ERR =>

"Попытка добавить атрибут, который уже используется",

DOM_INVALID_STATE_ERR => // DOM2

"Попытка использовать недоступный объект",

DOM_SYNTAX_ERR => // DOM2

"Задана неверная строка",

DOM_INVALID_MODIFICATION_ERR => // DOM2

"Попытка изменения типа объекта",

DOM_NAMESPACE_ERR => // DOM2

"Создать или изменить объект в данной области имен невозможно",

DOM_INVALID_ACCESS_ERR => // DOM2

"Оператор или операция не применимы к данному объекту", DOM_VALIDATION_ERR => // DOM3

"Попытка добавления или удаления узла приводит к ". "некорректному документу",

);

810

Часть VI. XML в PHP 5

$message = @$rusmes[$code]; if (!$message)

$message("Неизвестный код ошибки $code"); return $message . "\n";

}

В листинге приведен текст функции, переводящей код исключительной ситуации

в ее описание на русском языке. Данную функцию можно использовать для поясне-

ния содержания обнаруженной ситуации (листинг 38.32).

Листинг 38.32. Файл exceptions/exception2.php

<?php ## Формирование текста обнаруженной ошибки. require_once 'rusexceptions.inc'; $xml="<root><child/></root>";

try {

$dom1 = new domDocument(); $dom1->loadXML($xml); $dom2 = new domDocument(); $dom2->loadXML($xml);

$root1 = $dom1->documentElement; $child1 = $root1->firstChild; $root2 = $dom2->documentElement; $root2->appendChild($child1);

}catch (domException $e) {

$mes = "\nОшибка в строке ". $e->getLine(). " скрипта " . $e->getFile() . ":\n";

$mes .= $e->__toString() . "\n";

$mes .= 'Причина ошибки: ' . domexeptionmessage($e->code);

throw new Exception($mes);

}

В данном сценарии при возникновении исключительной ситуации в переменную $mes записывается текст ошибки с пояснениями на русском языке и повторно вызы-

вается исключительная ситуация со сформированным текстом.

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

рий, приведенный в листинге 38.33.

Листинг 38.33. Файл exceptions/exception3.php

<?php ## Пример загрузки некорректного XML-документа методом loadXML(). $xml = "<root><child11/><child2/></root1>";

$dom = new domDocument(); $ret = $dom->loadXML($xml); echo $dom->saveXML();

Загружаемый XML-документ имеет неверный закрывающий тег </root1>. Метод loadXML() при загрузке этого документа выведет предупреждающее сообщение и

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

Глава 38. DOM2 — пространства имен

811

Листинг 38.34. Результат вызова сценария exception3.php

Warning: domdocument::loadXML(): Opening and ending tag mismatch: root line 1 and root1 in Entity, line: 1 in /home/kaf/php5/bo

ok/partM/files/dom2/exceptions/exception3.php on line 4

<?xml version="1.0"?>

Часто в таких случаях нужно либо немедленно завершить выполнение программы,

либо произвести дополнительные действия по корректировке обрабатываемого документа. Тогда необходимо использовать механизм установки собственного обработчика ошибок (листинг 38.35).

Листинг 38.35. Файл exceptions/exception4.php

<?php ## Пример замены функции обработки ошибок. $xml = "<root><child11/><child2/></root1>"; $dom = new domDocument();

$old_error_handler = set_error_handler('domerrorhandler'); $ret = $dom->loadXML($xml);

echo $dom->saveXML(); /**

*

Замещающая

функция обработки ошибок.

*

@param int

$errno

— номер ошибки

*@param string $errstr — описание ошибки

*@param string $errfile — файл, где обнаружена ошибка

*@param int $errline — номер ошибочной строки

*/

function domerrorhandler($errno, $errstr, $errfile, $errline) { $mes = "\nОшибка загрузки документа.

Файл: $errfile. Строка: $errline. Код:$errno Ошибка:\n $errstr";

throw new Exception($mes);

}

Функция set_error_handler() определяет пользовательскую функцию обработки

ошибок domerrorhandler(). В этом случае при возникновении ошибочной ситуации управление передается указанной функции вместе с параметрами, определяющими код ошибки ($errno), текст ошибки ($errstr), файл и строку, где обнаружена ошибка ($errfile и $errline). Функция может проанализировать данную информацию и

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

Резюме

В данной главе мы рассмотрели основные классы (интерфейсы) и методы стандарта DOM2. Для демонстрации каждого свойства в главе приведены небольшие законченные скрипты, пользуясь которыми читатель может написать свои собственные

программы.

ГЛАВА 39

DOM3 и другие стандарты

Листинги данной главы можно найти в подкаталоге xml/dom3.

Спецификации DOM3 получили статус рекомендованного стандарта сравнительно недавно — 7 апреля 2004 года. Если говорить о PHP 5, то в данный момент (релиз 5.1.0-dev) лишь небольшая часть модулей стандарта DOM3 реализована в библиотеке libxml2, которая лежит в основе расширений языка, работающих со стандартом XML. В следующих релизах PHP набор поддерживаемых модулей расширится, но ожидать полной реализации всех модулей DOM3 не стоит, т. к. часть из них касается методов отображения XML-документов и их интерактивной корректировки. Это сфера интернет-браузеров, а не языков, обрабатывающих XML-документы.

В данной главе мы коснемся темы основных модулей и интерфейсов стандарта DOM3 и рассмотрим дополнительные методы, поддерживаемые в DOM-расширении языка PHP 5 и не входящие в DOM-стандарты, но значительно упрощающие поддержку HTML-документов.

Модули стандарта DOM

Уже начиная со второго релиза DOM, выяснилось, что уместить все возникающие стандарты в один документ стало невозможно. Начали возникать различные модули, отображающие определенный аспект обработки XML-документов. Список данных модулей приведен в листинге 39.1.

Листинг 39.1. Список модулей стандарта DOM

<?xml version="1.0" encoding="KOI8-R"?> <Core name="Ядро">

<XML name="XML"/> <HTML name="HTML"/> <Xpath name="Xpath"/>

<Traversal name="Обход XML-дерева"/> <Range name="Выборка частей документа"/> <Validation name="Проверка"/>

<LS name="Загрузка и выгрузка">

Глава 39. DOM3 и другие стандарты

813

<LSAsync name="Асинхронная загрузка и выгрузка"/> </LS>

<DocumentLS name="Загрузка и выгрузка документа"/> <ElementLS name="Загрузка и выгрузка элемента"/> <Views name="Отображения"/>

<Stylesheets name="Стили"> <CSS name="Стили CSS"/> <CSS2 name="Стили CSS2"/>

</Stylesheets>

<Events name="События">

<HTMLEvents name="События стандарта HTML"/>

<UIEvents name="События пользовательского интерфейса"> <KeyboardEvents name="События клавиатуры"/> <MouseEvents name="События мыши"/>

<TextEvents name="Текстовые события"/> </UIEvents>

<MutationEvents name="События модификации"> <MutationNameEvents name="Именованные события модификации"/>

</MutationEvents>

</Events>

</Core>

В листинге приведена иерархия модулей, описанная в формате XML. Если вы еще не можете свободно читать XML-формат, то обратитесь к рис. 39.1, где отображена иерархия модулей DOM.

Рассмотрим предназначение данных модулей (стандартов):

Core — основные интерфейсы DOM: DOMException, DOMImplementation,

DocumentFragment, Document, Node, NodeList, NamedNodeMap, CharacterData, Attr,

Element, Text и Comment;

XML — дополнительные интерфейсы узлов следующих типов: CDATASection,

DocumentType, Notation, Entity, EntityReference и ProcessingInstruction;

HTML — интерфейсы, разработанные для HTML-элементов: HTMLHtmlElement,

HTMLHeadElement и HTMLParagraphElement;

XPath — интерфейс для выборки элементов по шаблону пути;

Traverse — интерфейс для обхода дерева XML-документа;

Range — интерфейс для адресации фрагмента (range) документа, причем как начало, так и конец фрагмента может не совпадать с узлом и располагаться внутри текстового узла;

Validation — интерфейс для динамического изменения содержания и структуры XML-документа с поддержкой его корректности (valid);

LS (Load and Save) — интерфейс для загрузки (сериализации) DOM-дерева из текстового представления XML-документа и сохранения (десериализации) DOMдерева в текстовое представление;

LSAsync — асинхронный режим интерфейса LS;

DocumentLS — интерфейс для загрузки и сохранения XML-документа целиком;

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.