Ключевые слова:xml, php, (найти похожие документы)
From: Александр Неткачев <alex at devlink.crimea.ua>
Newsgroups:
Date: Mon, 19 Apr 2004 14:31:37 +0000 (UTC)
Subject: Альтернатива DOM XML на PHP (PHP4)
Оригинал статьи находится на http://devlink.crimea.ua
Для читателей
-------------
Материал этой статьи в первую очередь пригодится тем, кто ищет
альтернативу XML DOM расширению PHP и использует PHP 4. Начинающие
изучать XML или использующие XML программисты на PHP могут также
почерпнуть для себя множество полезного из материала этой статьи.
Предыстория
-----------
Изучив DOM XML расширение в PHP 4, я столкнулся с некоторыми трудностями
в его применении. Во-первых, это его статус экспериментального, в
результате чего некоторые провайдеры это расширение отключают.
Во-вторых, некоторое несоответствие в именовании методов и свойств с
W3C DOM Specification (href="http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226)
В-третьих, в PHP 5 расширение изменили с целью большего совпадения с
W3C DOM спецификацией, что автоматически делает его более привлекательным
объектом для изучения.
Но желание применить DOM для работы с XML документами было очень
большим. Кстати, DOM также предполагалось использовать как основу для
планируемой темплейтной библиотеки. Это привело к тому, что
сформировалось решение написать собственную простую реализацию DOM XML
на PHP 4, которая содержит только функции, необходимые для обхода XML
дерева и для простых его модификаций. Но то, что реализовано, должно
быть как можно более приближено к W3C Document Object Model (DOM)
Level 3 Core Specification (http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226)
Потенциал подобной реализации DOM достаточно велик, поскольку до PHP 5
еще остается время, а использовать и обрабатывать XML тем или иным
образом приходится достаточно часто.
Основа XML Document Object Model
--------------------------------
Все варианты организации XML дерева основаны следующих правилах: a) узел
дерева связан с несколькими потомками и б) два узла не могут быть
связанны с одним и тем же потомком. Для задания потомков узла можно
использовать список или массив динамической длины. Данные узла и
указатели на потомков можно хранить в структуре, классе или массиве.
Дополнительно, узел дерева может содержать информацию о предке, двух
соседних узлах и корне.
Спецификация DOM не определяет внутренний способ хранения элементов, но
задает методы обращения к ним, предполагая, что язык реализации
поддерживает ООП.
Основой W3C XML DOM является интерфейс Node, который и представляет
собой узел дерева и создается почти для всех составляющих XML документа,
в том числе для его тегов, атрибутов, текста, CDATA, комментариев и
других. Основные его элементы следующие:
// некоторые константы, свойства, методы и исключения (Exceptions)
// не упомянуты с целью выделить только важные для конкретной реализации
interface Node {
// Типы составляющих XML документа,
const unsigned short ELEMENT_NODE = 1;
const unsigned short TEXT_NODE = 3;
const unsigned short DOCUMENT_NODE = 9;
// Свойства и методы XML узла. Для большинства назначение
// очевидно из названия. Если не очевидно, воспользуйтесь
// их описанием на W3C.
readonly attribute DOMString nodeName;
attribute DOMString nodeValue;
readonly attribute unsigned short nodeType;
readonly attribute Node parentNode;
readonly attribute NodeList childNodes;
readonly attribute Node firstChild;
readonly attribute Node nextSibling;
readonly attribute NamedNodeMap attributes;
readonly attribute Document ownerDocument;
Node appendChild(in Node newChild);
boolean hasChildNodes();
};
Некоторые вспомогательные или производные от Node типы данных и
интерфейсы:
// некоторые константы, свойства, методы и исключения (Exceptions)
// не упомянуты с целью выделить только важные для конкретной реализации
interface NodeList {
Node item(in unsigned long index);
readonly attribute unsigned long length;
};
interface NamedNodeMap {
Node getNamedItem(in DOMString name);
Node setNamedItem(in Node arg)
raises(DOMException);
Node removeNamedItem(in DOMString name)
raises(DOMException);
Node item(in unsigned long index);
readonly attribute unsigned long length;
};
interface Element : Node {
DOMString getAttribute(in DOMString name);
void setAttribute(in DOMString name,
in DOMString value);
};
interface CharacterData : Node {
attribute DOMString data;
void appendData(in DOMString arg);
};
interface Document : Node {
readonly attribute Element documentElement;
attribute DOMString documentURI;
Element createElement(in DOMString tagName);
Text createTextNode(in DOMString data);
};
interface Text : CharacterData {
};
Реализация XML DOM на PHP4
--------------------------
Прежде всего, W3C DOM подразумевает, что язык реализации поддерживает
ООП. Наиболее распространенные и известные языки, поддерживающие ООП -
это C++, Java. Далее идут .NET, Delphi (Pascal), Python и другие,
включая PHP4, который, прямо скажем, не очень приспособлен к ООП. Но,
кое что из него выжать можно, в чем мы и убедимся.
Для начала, попробуем выбрать только самое необходимое из методов W3C
DOM и определить некоторые правила реализации:
* NamedNodeMap и NodeList, по всей видимости, не нужны. Их с успехом
заменит обычный PHP массив.
* DOMException не является необходимым. Достаточно просто вернуть
NULL.
* Интерфейсы CharacterData и Text не кажутся столь необходимыми.
Перенесем и методы непосредственно в Node.
* Доступ к свойствам Node сделаем через методы get<Property> и
set<Property>, в лучших традициях ООП - не использовать public
переменные класса.
* Доступ к заявленным read-only свойствам интерфейса Node сделаем
read-write - надо же их как-то устанавливать.
* getAttribute и setAttribute добавим непосредственно у Node - таким
образом Element нам тоже не понадобится.
* Полученные классы являются составной частью библиотеки для
сайтостроительства, и поэтому будем добавлять к константам и именам
класса приставку "SF" (SiteFramework).
Аккуратно трансформировав DOM интерфейсы используя заданые правила,
получаем необходимые классы и константы:
define('SFNODE_ELEMENT', 1);
define('SFNODE_TEXT', 2);
define('SFNODE_DOCUMENT', 3);
class SFNode {
function setNodeName($nodeName);
function getNodeName();
function setNodeValue($value);
function getNodeValue();
function setNodeType($nodeType);
function getNodeType();
function setParentNode(&$node);
function &getParentNode();
function &getChildNodes();
function &getFirstChild();
function &getNextSibling();
function setNextSibling(&$node);
function &getAttributes();
function setOwnerDocument(&$node);
function &getOwnerDocument();
function &appendChild(&$newChild);
function hasChildNodes();
function setAttribute($name, $value);
function getAttribute($name);
function appendData($str);
}
class SFDocument extends SFNode {
function &getDocumentElement();
function setDocumentURI($documentURI);
function getDocumentURI();
function &createElement($tagName);
function &createTextNode($data);
}
Добавив необходимый код в эти методы получаем аккуратную и необходимую
реализацию DOM на PHP. Полученный код классов, загрузчик XML и пример
использования можно скачать http://devlink.crimea.ua/files/php_xml.zip (5.2 kB)
Примеры использования PHP реализации XML DOM
--------------------------------------------
Простой XML загрузчик
Для загрузки XML воспользуемся expat
(http://sourceforge.net/projects/expat/) XML парсером. Основная идея
этого парсера состоит в том, что он разбирает XML документ и
вызывает callback функции для начала тега, конца тега, текста и т.д.
Что именно делать с тегами и атрибутами решает разработчик,
реализовывая произвольный код в вызываемых парсером функциях.
Лучше один раз увидеть, чем сто раз услышать, поэтому предлагаю Вашему
вниманию основу класса для парсинга XML. Полный вариант загрузчика Вы
можете скачать http://devlink.crimea.ua/files/php_xml.zip (5.2 kB).
class SFXmlParser {
var $xmlDocument;
var $currentNode;
function parseString($data) {
$this->currentNode = null;
$this->xmlDocument = new SFDocument();
$xml_parser = xml_parser_create('utf-8');
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
xml_set_object($xml_parser, $this);
xml_set_element_handler($xml_parser, 'startElementHandler',
'endElementHandler');
xml_set_character_data_handler($xml_parser, 'characterDataHandler');
xml_set_default_handler($xml_parser, 'defaultHandler');
if (!xml_parse($xml_parser, $data, TRUE)) {
return new SFException('ERROR: XML error: '
. xml_error_string(xml_get_error_code($xml_parser))
. ' at line '
. xml_get_current_line_number($xml_parser));
}
xml_parser_free($xml_parser);
return $this->xmlDocument;
}
function startElementHandler($parser, $name, $attrs) {
if (!$this->currentNode)
$this->currentNode =& $this->xmlDocument->appendChild(
$this->xmlDocument->createElement($name));
else {
if ($this->currentNode->getNodeType() == SFNODE_TEXT)
$this->currentNode =& $this->currentNode->getParentNode();
$this->currentNode =& $this->currentNode->appendChild(
$this->xmlDocument->createElement($name));
}
foreach (array_keys($attrs) as $key)
$this->currentNode->setAttribute($key, $attrs[$key]);
}
function endElementHandler($parser, $name) {
if ($this->currentNode->getNodeType() == SFNODE_TEXT)
$this->currentNode =& $this->currentNode->getParentNode();
$this->currentNode =& $this->currentNode->getParentNode();
}
function characterDataHandler($parser, $text) {
if ($this->currentNode->getNodeType() == SFNODE_TEXT)
$this->currentNode->appendData($text);
else
$this->currentNode =& $this->currentNode->appendChild(
$this->xmlDocument->createTextNode($text));
}
function defaultHandler($parser, $data) {
return NULL;
}
}
class SFException {
var $message;
function SFException($message) { $this->message = $message; }
function getMessage() { return $this->message; }
}
// Пример использования
$xmlParser = new SFXmlParser();
$xmlDoc =& $xmlParser->parseString('<urls>'
. '<url>http://php.net/</url>'
. '<url>http://phpclub.ru/</url></urls>');
if (is_a($xmlDoc, 'SFException'))
exit($xmlDoc->getMessage());
Обход XML документа
Задача обхода всех узлов является классической при работе с деревом XML
документа. Существует, также, 2 варианта решения этой задачи:
рекурсивный и не рекурсивный.
Приведенный пример рекурсивного обхода вызывает callback функции для
каждого узла, начиная некоторого данного узла $node:
function walkthrough(&$node, $callbacks) {
if ($node->getNodeType() == SFNODE_TEXT) {
if (function_exists($fName = $callbacks['text']))
$fName($node);
} else {
if ($node->getNodeType() == SFNODE_ELEMENT
&& function_exists($fName = $callbacks['beginTag']))
$fName($node);
if ($node->hasChildNodes())
for($n =& $node->getFirstChild(); $n != NULL; $n =& $n->getNextSibling())
walkthrough($n, $callbacks);
if ($node->getNodeType() == SFNODE_ELEMENT
&& function_exists($fName = $callbacks['endTag']))
$fName($node);
}
}
Преобразование XML документа в HTML
Используя пример загрузки XML документа и функцию обхода можно составить
небольшую программу, обходит XML документ и для каждого узла строит HTML
представление.
Приведенный пример преобразует список ссылок в вида <url>http://mysite/</url> в
<a href="http://mysite/">http://mysite/</a>
// тут код предыдущих примеров
function printBeginTag(&$node) {
if ($node->getNodeName() == 'url') {
$urlNode =& $node->getFirstChild();
$url = $urlNode->getNodeValue();
print '<a href="' . $url . '">' . $url . "</a>\n";
}
}
walkthrough($xmlDoc->getDocumentElement(), array('beginTag' => 'printBeginTag'));
Заключение
----------
XML набирает популярность день за днем и в последнее время становится
стандартным методом хранения информации. Например, документ OpenOffice
(в котором я набираю текст этой статьи) является XML файлом. Можно
предположить, что если Вы будете и дальше заниматься программированием,
то все больше и больше Ваших задач будут связаны с построением и
обработкой XML документов.
Если предложенное решение Вам пригодится в некоторых из них, я буду рад,
если Вы мне об этом сообщите. Если у Вас есть комментарии или
предложения, касательно материала статьи, Вы всегда можете сообщить о
них.
Ссылки по теме
--------------
http://www.w3c.org/ - World Wide Web Consortium.
http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226 - W3C DOM Specification
http://www.php.net/ - PHP language site.
href="http://phpclub.ru/ - PHP Club, Российское сообщество PHP разработчиков.
http://detail.phpclub.net/article/2003-05-12 - XML: спецификация и функции DOM в PHP / Дмитрий Лебедев
Copyrights
----------
The article is partly based on the W3C Document <a target="_blank" href="http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226">Object Model (DOM)
Level 3 Core Specification Working Draft</a> of 26 February 2003.
Copyright 2003 World Wide Web Consortium, (Massachusetts Institute of
Technology, European Research Consortium for Informatics and
Mathematics, Keio University). All Rights Reserved.
http://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231
The article is partly based on the PHP, freely available from http://www.php.net/
Примечания
----------
Основаная цель проекта http://phpmyxml.sourceforge.net/ очень схожа с
идеей этой статьи, но значительно шире - реализация DOM, XPath и XSLT
рекоммендаций на PHP. Я рекомендую попробовать использовать его, если
функциональность классов представленных в этой статье является
недостаточной для Вашего проекта.
Сейчас работаю над web-приложением с интенсивным интерактивным (относительно сложные формы),
и пытаюсь среди прочего сделать какую-то реализацию XForms.
И в расширениях PHP4 и в вашей реализации крайне нехватает поддержки xpath, namespaces, xslt.
С этими аспектами domxml был бы на порядок более полезен.