PHP4 без XML-парсера, плюс XPath

Я недавно написал простенький парсер XML для PHP 4.3+, для случаев, когда никакого встроенного парсера XML на хостинге не оказалось.

А вчера мне подумалось, что парсить XML в дерево это, конечно, замечательно, но пользоваться этим всё равно неудобно. Было бы неплохо дать возможность получать произвольные узлы так же просто, как это делается при помощи XPath.

Весь XPath я, конечно, реализовывать не буду, да мне столько и не надо. В общем, написал класс, который используется в паре VerySimple_XMLParser. Реализованы получение потомков тега, «*», «node()» и «text()». Это всё, но мне хватит.
// Simple XPath for VerySimple_XMLParser
// http://bolknote.ru/
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 в дерево, а помещать каждый элемент по специального вида пути, который потом будет проверен на совпадение по регулярному выражению, приготовленному из запроса.
26 сентября 2010 16:34

Дмитрий Фантазеров (Смирнов) (fantaseour.ya.ru)
26 сентября 2010, 19:47

Полдела сделано :) теперь еще написать лексический парсер для выражений xsl:select и xsl:if и будет XSLT :)

bolk (bolknote.ru)
26 сентября 2010, 19:52, ответ предназначен fantaseour.ya.ru:

Да нафига он мне :)

Я же свои задачи решаю, а не пишу сферического коня в вакууме.

Дмитрий Фантазеров (Смирнов) (fantaseour.ya.ru)
26 сентября 2010, 20:15

да я так, больше даже с самоиронией. я для своего урезанного xslt делал естественно и XPath -- перед выполнением строил дерево узлов исходного xml, и тоже искал по пути. И еще кешировал индексы узлов, которые уже были найдены.

Та еще городушка была конечно.

bolk (bolknote.ru)
26 сентября 2010, 21:46, ответ предназначен fantaseour.ya.ru:

У меня цели попроще и производительность можно не оптимизировать.

А что за проект такой, где понадобилось свой XSLT-процессор писать?

Дмитрий Фантазеров (Смирнов) (fantaseour.ya.ru)
28 сентября 2010, 15:27, ответ предназначен bolk (bolknote.ru):

Да банальная cms. Казалось что xml-xslt решит сразу кучу проблем. Оказалось однако, что sablotron встает с таким скрипом и не всегда, что решил подстраховаться -- сделать усеченный парсер.

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)