<?php
#
# IBCTS 3.0
# Information Bot Carrier Transporting Service
#
# Copyright (C) 2007-2008 HSDN <info@hsdn.org>
# Copyright (C) 2007 nepedeng.org
# http://www.hsdn.org
#
# @descr Модуль генерация и парсинга деревьев XML
# @version 3.0.3
/**
 * Класс генерация и парсинга деревьев XML
 *
 * @access public
 */
class XmlTree
{
    /**
     * Отпарсить дерево XML
     *
     * @param string $raw
     * @return array
     */
    function parseTree($raw)
    {
        $raw = preg_replace("#<\\?xml.*?\\?>#", '', $raw);
        $root = new XmlElement(null);
        if (is_string($err = XmlTree::parseTreeElement(null, 0, $raw, $root))) return $err;
        return $root->next();
    }
    /**
     * Парсинг элемента дерева
     *
     * @param int $wait_for
     * @param int $i
     * @param string &$raw
     * @param string &$curr
     * @return mixed
     */
    function parseTreeElement($wait_for, $i, &$raw, &$curr)
    {
        $raw_len = strlen($raw);
        $spacer_reached = $tag_def_opened = $tag_closure_def = $attr_val_started = false;
        $attr_delim = null;
        $tag_name = $current_attr = $current_attr_val = '';
        $tag_attr = array();
        $innerText = '';
        for ( ; $i < $raw_len; $i++)
        {
            switch (1)
            {
                default:
                    switch (@$raw{$i})
                    {
                        case ' ': case "\n": case "\r": case "\t":
                            if (!$tag_def_opened) break;
                            $spacer_reached = true;
                            break;
                        case '<':
                            $innerText = trim(XmlTree::ent2byte($innerText));
                            if (strlen($innerText) > 0)
                            {
                                $curr->insertChild($innerText);
                                $innerText = '';
                            }
                            if ($attr_val_started) break;
                            $tag_def_opened = true;
                            break;
                        case '>':
                            if (!$tag_def_opened | $attr_val_started) break;
                            if (!empty($current_attr)) $tag_attr[$current_attr] = $current_attr_val;
                            $i++;
                            if ($tag_closure_def & ($wait_for == $tag_name) & empty($current_attr))
                            {
                                break 3;
                            }
                            else
                            {
                                $new_element = new XmlElement($tag_name, $tag_attr);
                                if (!$tag_closure_def)
                                {
                                    if (is_string($err = XmlTree::parseTreeElement($tag_name, &$i, $raw, $new_element))) { return $err; }
                                }
                                $curr->insertChild($new_element);
                            }
                            $spacer_reached = $tag_def_opened = $tag_closure_def = $attr_val_started = false;
                            $attr_delim = null;
                            $tag_name = $current_attr = $current_attr_val = '';
                            $tag_attr = array();
                        case '/':
                            if (!$tag_def_opened) break;
                            $tag_closure_def = true;
                            break;
                        case '"': case "'":
                            if (!$tag_def_opened) break;
                            if (($attr_delim == null) & $attr_val_started)
                            {
                                $attr_delim = $raw{$i};
                            }
                            else
                            {
                                $attr_delim = null;
                                $attr_val_started = false;
                            }
                            break;
                        case '=':
                            if (!$tag_def_opened) break;
                            $attr_val_started = true;
                            break;
                        default:
                            if ($tag_def_opened)
                            {
                                if (!$spacer_reached & !$attr_val_started)
                                {
                                    if (!preg_match("#([^<>/\\s]+?)(\\s+|[<>/])#", $raw, $matches, 0, $i)) return "XML parse error at offset $i\n";
                                    $tag_name = $matches[1];
                                    $i += strlen($matches[0])-2;
                                    $matches = null;
                                    $spacer_reached = true;
                                }
                                else if ($spacer_reached & !$attr_val_started & !empty($tag_name))
                                {
                                    if (!preg_match("#([^=<>/\\s]+?)=#", $raw, $matches, 0, $i)) return "XML parse error at offset $i\n";
                                    if (!empty($current_attr)) $tag_attr[$current_attr] = $current_attr_val;
                                    $current_attr = $matches[1];
                                    $i += strlen($matches[0])-1;
                                    $matches = null;
                                    $attr_val_started = true;
                                }
                                else if ($attr_val_started)
                                {
                                    if (!preg_match("#(.*?)" . preg_quote($attr_delim,'#'). "#s", $raw, $matches, 0, $i)) return "XML parse error at offset $i\n";
                                    $current_attr_val = XmlTree::ent2byte($matches[1]);
                                    $i += strlen($matches[0])-2;
                                    $matches = null;
                                    $attr_val_started = false;
                                }
                            }
                            break;
                    }
                    if (!$tag_def_opened) { $innerText .= @$raw{$i}; continue;}
            }
        }
        return 1;
    }
    /**
     * Конвертер спецификации в символы
     *
     * @param string $r
     * @return string
     */
    function ent2byte($r)
    {
        $r = str_replace
        (
            array('&', '<', '>', '"'),
            array('&', '<', '>', '"'),
            $r
        );
        return $r;
    }
    /**
     * Построение дерева XML
     *
     * @param resource $root
     * @param string $xmlVer
     * @param string $encoding
     * @return string
     */
    function buildTree($root, $xmlVer = '1.0', $encoding = 'windows-1251')
    {
        $ret  = '<?xml';
        $ret .= XmlTree::buildAttributes(array('version' => $xmlVer, 'encoding' => $encoding));
        $ret .= "?>\n";
        $ret .= XmlTree::buildElement($root);
        return $ret;
    }
    /**
     * Построение элемента дерева
     *
     * @param mixed $curr
     * @return string
     */
    function buildElement($curr)
    {
        if (!is_object($curr))
        {
            return htmlspecialchars($curr);
        }
        $ret  = '';
        $ret .= "<" . $curr->name;
        $ret .= XmlTree::buildAttributes($curr->attr);
        if (empty($curr->childs) && empty($curr->cdata))
        {
            $ret .= "/>\n";
        }
        else
        {
            $ret .= ">";
            $ins = '';
            while (!is_null($nextChild = $curr->next())) $ins .= XmlTree::buildElement($nextChild);
            if (ereg("^<",$ins)) $ret .= "\n".$ins; else $ret .= $ins;
            if (!empty($curr->cdata))
            {
                $ret .= "<![CDATA[ " . $curr->cdata . "]]>";
            }
            $ret .= "</" . $curr->name . ">\n";
        }
        return $ret;
    }
    /**
     * Построить атрибуты
     *
     * @param array $attr
     * @return string
     */
    function buildAttributes($attr)
    {
        $ret  = "";
        foreach ($attr as $name => $value)
        {
            $ret .= ' ';
            $ret .= $name;
            $ret .= '="' . htmlspecialchars($value) . '"';
        }
        return $ret;
    }
}
/**
 * Класс построение XML-элемента
 *
 * @access public
 */
class XmlElement
{
    /*
     * Дочерние элементы
     * @var array
     */
    var $childs = array();
    /*
     * Текущие аттрибуты
     * @var array
     */
    var $attr = array();
    /*
     * Имя элемента
     * @var string
     */
    var $name = null;
    /*
     * Содержимое (CDATA секция)
     * @var string
     */
    var $cdata = null;
    /*
     * Указатель на текущую "дочку"
     * @var int
     */
    var $childId = -1;
    /**
     * Создает элемент XML с заданным именем и (если нужно) аттрибутами
     *
     * @param string $name
     * @param array $attr
     */
    function XmlElement($name, $attr = array())
    {
        $this->name = $name;
        $this->attr = $attr;
    }
    /**
     * Добавляет дочерний элемент к текущему
     *
     * @param array $child
     * @return array
     */
    function insertChild($child)
    {
        $this->childs[] = $child;
        return $this;
    }
    /**
     *
     * @return array
     */
    function next()
    {
        $this->childId++;
        if (!isset($this->childs[$this->childId])) return null;
        return $this->childs[$this->childId];
    }
}
$xml = file_get_contents('http://www.vfose.ru/rss.php');
$root = XmlTree::parseTree($xml);
print_r($root);
?>