<?php
/*
* XmlParser.class.php -- Version 14-Jan-2006
*
* Copyright (c) 2004-2006 Jochen Kupperschmidt
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* The above license is the MIT License. It was copied from the website of the
* Open Source Initiative: http://www.opensource.org/licenses/mit-license.php
* _ _
* | |_ ___ _____ ___ _ _ _ ___ ___| |_
* | | . | | ._| | | | . | _| . /
* |_|_|___|_|_|_|___|_____|___|_| |_|_\
* http://homework.nwsnet.de/
*
* This can be seen as XML SAX parser framework. With it, you can easily create
* parsers for your custom XML tags.
*
* To create a parser, write a subclass of the XmlParser class and add methods
* which names start with 'handle_', followed by the tag name it should react
* to. When the parser reaches a closing tag, the corresponding method will be
* called. Its parameters will be an associative array containing the
* attributes firstly and a string containing the character data (the data
* enclosed by start and end tag of an element) secondly.
*
* Example usage:
*
* # Include the parser framework.
* require_once('XmlParser.class.php');
*
* # Subclass it.
* class XmlLinkParser extends XmlParser {
*
* # This would react on
* # <link href="http://www.example.com/">Example.com</link>
* # and return
* # <a href="http://www.example.com/"
* # target="_blank"
* # title="Visit Example.com"
* # >Example.com (http://www.example.com/)</a>
* function handle_link($attr, $cdata) {
* return sprintf(
* '<a href="%s" target="_blank" title="Visit %s">%s (%s)</a>',
* $attr['href'], $cdata, $cdata, $attr['href']);
* }
* }
*
* # Instanciate the custom parser.
* $parser = new XmlLinkParser();
*
* # Parse a file with XML content and display the result.
* echo $parser->parseFile('example.xml');
*
* # Parse a string and display the result.
* echo $parser->parseString('<div>
* <h1>Some links</h1>
* <ul>
* <li><link href="http://www.example.com/one/">Example #1</link></li>
* <li><link href="http://www.example.com/two/">Example #2</link></li>
* <li><link href="http://www.example.com/three/">Example #3</link></li>
* </ul>
* </div>
* ');
*/
# A stack.
#
# Elements can be pushed upon or popped off the top.
class Stack {
var $stack = array();
# Place an element on the stack.
function push($data) {
array_push($this->stack, $data);
}
# Take an element off the stack.
function pop() {
if (! $this->stack) {
trigger_error('Stack buffer underrun.', E_USER_ERROR);
}
return array_pop($this->stack);
}
}
# The basic SAX parser.
#
# Subclasses may implement methods which names begin with "handle_", followed
# by the tag name to be reacted to.
#
# Example:
# function handle_something($attr, $cdata) { ... }
# It will be invoked when the closing part of the <something> element is found.
class XmlParser {
var $stack;
# Read XML from file.
function parseFile($filename) {
if (! file_exists($filename)) {
trigger_error('File not found.', E_USER_WARNING);
return False;
}
return $this->parseString(implode('', file($filename)));
}
# Read XML from string.
function parseString($data) {
$this->stack = new Stack();
$this->stack->push(array('content' => ''));
$parser = xml_parser_create('iso-8859-1');
xml_set_object($parser, &$this);
xml_set_element_handler($parser, 'startElement', 'endElement');
xml_set_character_data_handler($parser, 'characters');
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
if (! xml_parse($parser, $data)) {
$errorCode = xml_get_error_code($parser);
$errorString = xml_error_string($errorCode);
$error = sprintf('Error parsing XML data (#%d: %s).', $errorCode,
$errorString);
trigger_error($error, E_USER_WARNING);
}
xml_parser_free($parser);
$first = $this->stack->pop();
return $first['content'];
}
# ---------------------------------------------------------------- #
# Called when an element starts.
function startElement($parser, $tag, $attributes) {
$this->stack->push(array(
'attributes' => $attributes,
'content' => ''
));
}
# Called when character data is found.
function characters($parser, $cdata) {
$data = $this->stack->pop();
$data['content'] .= $cdata;
$this->stack->push($data);
}
# Called when an element ends.
function endElement($parser, $tag) {
$elementHandler = 'handle_' . strtolower($tag);
$data = $this->stack->pop();
if (method_exists($this, $elementHandler)) {
$buffer = call_user_func(array($this, $elementHandler),
$data['attributes'], $data['content']);
} else {
$buffer = '<' . $tag;
foreach ($data['attributes'] as $attr => $value) {
$buffer .= sprintf(' %s="%s"', $attr, $value);
}
if (0 == strlen($data['content'])) {
$buffer .= '/>';
} else {
$buffer .= sprintf('>%s</%s>', $data['content'], $tag);
}
}
$sublevel = $this->stack->pop();
$sublevel['content'] .= $buffer;
$this->stack->push($sublevel);
}
}
?>