<?php
class XmlParser {
#region "XML parser variables"
var $parser;
var $data = [];
var $stack = [];
var $keys;
#endregion
var $type;
function __construct($url, $type = 'url') {
$this->type = $type;
$this->url = $url;
$this->parse();
}
public static function LoadFromURL($url) {
return (new XmlParser($url, "url"))->data;
}
public static function LoadFromXml($text) {
return (new XmlParser($text, "contents"))->data;
}
private function parse() {
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, 'startXML', 'endXML');
xml_set_character_data_handler($this->parser, 'charXML');
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
if ($this->type == 'url') {
$this->parseResource();
} else if ($this->type == 'contents') {
$this->parseContent();
} else {
self::error("Invalid data type!");
}
}
private function parseContent() {
$lines = explode("\n", $this->url);
$data = "";
foreach ($lines as $val) {
if (trim($val) == '') {
continue;
} else {
$data = $val . "\n";
}
if (!xml_parse($this->parser, $data)) {
$this->throwFileError();
}
}
}
private function throwFileError() {
$line = xml_get_current_line_number($this->parser);
$colm = xml_get_current_column_number($this->parser);
$msg = 'XML error at line %d column %d';
$msg = sprintf($msg, $line, $colm);
self::error($msg);
}
private function parseResource() {
if (!($fp = @fopen($this->url, 'rb'))) {
self::error("Cannot open {$this->url}");
}
$data = '';
while (($data = fread($fp, 8192))) {
if (!xml_parse($this->parser, $data, feof($fp))) {
$this->throwFileError();
}
}
}
private function startXML($parser, $name, $attr) {
$this->stack[$name] = [];
$keys = '';
$total = count($this->stack) - 1;
$i = 0;
foreach ($this->stack as $key => $val) {
if (count($this->stack) > 1) {
if ($total == $i) {
$keys .= $key;
} else {
$keys .= $key . '|';
}
} else {
$keys .= $key;
}
$i++;
}
if (array_key_exists($keys, $this->data)) {
$x = $this->data[$keys];
if (self::isSingleNode($x)) {
$this->data[$keys] = [$x, $attr];
} else {
$this->data[$keys][] = $attr;
}
} else {
$this->data[$keys] = $attr;
}
$this->keys = $keys;
}
private static function isSingleNode($x) {
if (empty($x) || count($x) == 0) {
return true;
}
$first = $x[array_keys($x)[0]];
$is = count($x) > 1 && is_string($first);
return $is;
}
private function endXML($parser, $name) {
end($this->stack);
if (key($this->stack) == $name) {
array_pop($this->stack);
}
}
private function charXML($parser, $data) {
if (trim($data) != '') {
$val = trim(str_replace("\n", '', $data));
if (self::isSingleNode($this->data[$this->keys])) {
$this->data[$this->keys]['data'] = $val;
} else {
$array = $this->data[$this->keys];
$array[count($array) - 1]['data'] = $val;
$this->data[$this->keys] = $array;
}
}
}
public static function error($msg) {
\dotnet::ThrowException("XmlParser error: $msg");
}
}
?>