<?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);
?>