PHP4 без XML-парсера, плюс XPath
Я недавно написал простенький парсер XML для PHP 4.3+, для случаев, когда никакого встроенного парсера XML на хостинге не оказалось.
Весь XPath я, конечно, реализовывать не буду, да мне столько и не надо. В общем, написал класс, который используется в паре VerySimple_XMLParser. Реализованы получение потомков тега, «*», «node()» и «text()». Это всё, но мне хватит.
// Simple XPath for VerySimple_XMLParser
// /
class VerySimple_XMLParser_Tree
{
var $path;
var $counter;
var $xpath;
function VerySimple_XMLParser_Tree()
{
$this->path = array('/');
$this->counter = 0;
$this->xpath = array();
}
function collect($type, $args)
{
$path = implode($this->path);
switch ($type) {
case 'TEXT':
$this->xpath[$path . $this->counter . '/text()/'] = $args;
$this->counter++;
break;
case 'TAGOPEN':
list($name, $attributes) = $args;
$tag = $this->counter . '/' . $name . '/';
$this->xpath[$path . $tag] = $attributes;
$this->counter = 0;
$this->path[] = $tag;
break;
case 'TAGCLOSE':
$this->counter = 1 + (int) array_pop($this->path);
break;
}
}
function halfxpath($path)
{
$keys = array_keys($this->xpath);
$trans = array(
'\/' => '\/\d+\/',
'\*' => '[^\/()]+',
'node\(\)' => '[^\/]+',
);
$filter = strtr(preg_quote($path, '/'), $trans);
$filtered = preg_grep("/^$filter\/$/su", $keys);
$nodes = array();
foreach ($filtered as $key) {
$nodes[$key] = $this->xpath[$key];
}
return $nodes;
}
}
// а вот так это можно использовать:
$xml =<<<XML
<root>
<a>
<b><c>Xa!</c></b>
<b>Hello</b>
</a>
<a name="value"/>
</root>
XML;
$tree = &new VerySimple_XMLParser_Tree();
$sax = &new VerySimple_XMLParser(array(&$tree, 'collect'));
$sax->parse($xml);
echo '<plaintext>';
var_dump($tree->halfxpath('/*/a/b/text()'));
var_dump($tree->halfxpath('/*/a/b/node()'));
var_dump($tree->halfxpath('/*/a/b/*'));
var_dump($tree->halfxpath('/root/a'));
Метод для запросов я назвал «halfxpath», намекая, что это не настоящий XPath, а лишь неполноценная эмуляция.
Основная идея, как видно из кода — не парсить XML в дерево, а помещать каждый элемент по специального вида пути, который потом будет проверен на совпадение по регулярному выражению, приготовленному из запроса.
Полдела сделано :) теперь еще написать лексический парсер для выражений xsl:select и xsl:if и будет XSLT :)
Комментарий для fantaseour.ya.ru:
Да нафига он мне :)
Я же свои задачи решаю, а не пишу сферического коня в вакууме.
да я так, больше даже с самоиронией. я для своего урезанного xslt делал естественно и XPath -- перед выполнением строил дерево узлов исходного xml, и тоже искал по пути. И еще кешировал индексы узлов, которые уже были найдены.
Та еще городушка была конечно.
Комментарий для fantaseour.ya.ru:
У меня цели попроще и производительность можно не оптимизировать.
А что за проект такой, где понадобилось свой XSLT-процессор писать?
Комментарий для Евгения Степанищева:
Да банальная cms. Казалось что xml-xslt решит сразу кучу проблем. Оказалось однако, что sablotron встает с таким скрипом и не всегда, что решил подстраховаться -- сделать усеченный парсер.