616 Zeilen
13 KiB
PHP
616 Zeilen
13 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Project:
|
||
|
* Contenido Content Management System
|
||
|
*
|
||
|
* Description:
|
||
|
* Contenido XML Parser
|
||
|
*
|
||
|
* Requirements:
|
||
|
* @con_php_req 5.0
|
||
|
*
|
||
|
*
|
||
|
* @package Contenido_XML
|
||
|
* @version 1.0.9
|
||
|
* @author Jan Lengowski
|
||
|
* @copyright four for business AG <www.4fb.de>
|
||
|
* @license http://www.contenido.org/license/LIZENZ.txt
|
||
|
* @link http://www.4fb.de
|
||
|
* @link http://www.contenido.org
|
||
|
* @since file available since contenido release <= 4.6
|
||
|
*
|
||
|
* {@internal
|
||
|
* created unknown
|
||
|
* modified 2008-06-30, Dominik Ziegler, add security fix
|
||
|
*
|
||
|
* $Id: class.xmlparser.php 374 2015-11-09 15:59:28Z oldperl $:
|
||
|
* }}
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
if(!defined('CON_FRAMEWORK')) {
|
||
|
die('Illegal call');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Class for parsing XML documents using SAX
|
||
|
*
|
||
|
* This class is a abstraction class for the PHP Expat XML functions.
|
||
|
*
|
||
|
* You can define handler functions/objects for start, end, PI and data sections (1.) or
|
||
|
* your can define path which will trigger the defined event when encountered (2.)
|
||
|
*
|
||
|
* Example:
|
||
|
*
|
||
|
* 1.) $parser->setEvents(array("startElement"=> "myFunction",
|
||
|
* "endElement"=> "myFunction",
|
||
|
* "characterData"=> "myFunction",
|
||
|
* "processingInstruction" => "myFunction");
|
||
|
*
|
||
|
* The value can also be an array with the object reference and the method to call.
|
||
|
* i.e. "startElement"=>array(&$myObj, "myMethod") instead of "startelement"=>"myFunction"
|
||
|
*
|
||
|
* 2.) $parser->setEvents(array("/root/foo/bar"=>"myFunction"));
|
||
|
*
|
||
|
* Valid array keys are: 'startElement', 'endElement', 'characterData', 'processingInstruction' and paths
|
||
|
* folowing the scheme '/root/element'. The path MUST begin from the root element and MUST start with '/'.
|
||
|
*
|
||
|
* The value can also be an array with the object reference and the method to call.
|
||
|
* i.e. "/foo/bar"=>array(&$myObj, "myMethod") instead of "/foo/bar"=>"myFunction"
|
||
|
*
|
||
|
* It has 3 public methods:
|
||
|
*
|
||
|
* setEventHandlers - Set specific handlers for the xml parser
|
||
|
* parseFile - Used to parse a XML file
|
||
|
* parse - Used to parse a XML string
|
||
|
*
|
||
|
* A small example:
|
||
|
*
|
||
|
* include ("class.xmlparser.php");
|
||
|
*
|
||
|
* // The XML String
|
||
|
* $xml = '
|
||
|
* <?xml version="1.0"?>
|
||
|
* <foo>
|
||
|
* <bar>some text</bar>
|
||
|
* <bar>another text</bar>
|
||
|
* </foo>';
|
||
|
*
|
||
|
* function myHandler($name, $attribs, $content)
|
||
|
* {
|
||
|
* echo "<b style='color:red'>HIT</b>: [ <b>$name</b> ] [ $content ]<br/>";
|
||
|
* }
|
||
|
*
|
||
|
* $parser = new XmlParser; // Parser instance
|
||
|
* $parser->setEventHandlers(array("/foo/bar"=>"myHandler")); // Define our handler
|
||
|
* $parser->parse($xml); // Parse the XML string
|
||
|
*
|
||
|
* Report bugs to: jan.lengowski@4fb.de
|
||
|
*
|
||
|
* @author Jan Lengowski <Jan.Lengowski@4fb.de>
|
||
|
* @copyright four for business AG <www.4fb.de>
|
||
|
* @version 1.0
|
||
|
*/
|
||
|
class XmlParser
|
||
|
{
|
||
|
/**
|
||
|
* XML Parser autofree
|
||
|
*
|
||
|
* @var bool
|
||
|
*/
|
||
|
var $autofree = true;
|
||
|
|
||
|
/**
|
||
|
* XML Parser object
|
||
|
*
|
||
|
* @var object
|
||
|
* @access private
|
||
|
*/
|
||
|
var $parser;
|
||
|
|
||
|
/**
|
||
|
* Error message
|
||
|
*
|
||
|
* @var string
|
||
|
* @access private
|
||
|
*/
|
||
|
var $error = '';
|
||
|
|
||
|
/**
|
||
|
* Element depth
|
||
|
* @var int
|
||
|
* @access private
|
||
|
*/
|
||
|
var $depth = -1;
|
||
|
|
||
|
/**
|
||
|
* Element counter
|
||
|
* @var int
|
||
|
* @access private
|
||
|
*/
|
||
|
var $count = 0;
|
||
|
|
||
|
/**
|
||
|
* Path counter
|
||
|
* @var int
|
||
|
* @access private
|
||
|
*/
|
||
|
var $pcount = 0;
|
||
|
|
||
|
/**
|
||
|
* Array for creating the path
|
||
|
* @var array
|
||
|
* @access private
|
||
|
*/
|
||
|
var $paths = array();
|
||
|
|
||
|
/**
|
||
|
* Data storage container for the path data
|
||
|
* @var array
|
||
|
* @access private
|
||
|
*/
|
||
|
var $pathdata = array();
|
||
|
|
||
|
/**
|
||
|
* String storing the active path
|
||
|
* @var string
|
||
|
* @access private
|
||
|
*/
|
||
|
var $activepath = '';
|
||
|
|
||
|
/**
|
||
|
* The active node
|
||
|
* @var string
|
||
|
* @access private
|
||
|
*/
|
||
|
var $activenode = '';
|
||
|
|
||
|
/**
|
||
|
* The defined events
|
||
|
* @var array
|
||
|
* @access private
|
||
|
*/
|
||
|
var $events = array();
|
||
|
|
||
|
/**
|
||
|
* Constructor function
|
||
|
*
|
||
|
* @access private
|
||
|
* @param string $sEncoding Encoding used when parsing files (default: UTF-8, as in PHP5)
|
||
|
* @return void
|
||
|
*/
|
||
|
function XmlParser($sEncoding = false)
|
||
|
{
|
||
|
if (!$sEncoding)
|
||
|
{
|
||
|
$sEncoding = "UTF-8";
|
||
|
}
|
||
|
|
||
|
$this->_init($sEncoding);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize the XML Parser object and sets all options
|
||
|
*
|
||
|
* @access private
|
||
|
* @param string $sEncoding Encoding used when parsing files (default: UTF-8, as in PHP5)
|
||
|
* @return void
|
||
|
*/
|
||
|
function _init($sEncoding = false)
|
||
|
{
|
||
|
if (!$sEncoding)
|
||
|
{
|
||
|
$sEncoding = "UTF-8";
|
||
|
}
|
||
|
// Create parser instance
|
||
|
$this->parser = xml_parser_create($sEncoding);
|
||
|
|
||
|
// Set parser options
|
||
|
xml_set_object($this->parser, $this);
|
||
|
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
|
||
|
xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $sEncoding);
|
||
|
|
||
|
xml_set_element_handler($this->parser, '_startElement', '_endElement');
|
||
|
xml_set_character_data_handler($this->parser, '_characterData');
|
||
|
xml_set_processing_instruction_handler($this->parser, '_processingInstruction');
|
||
|
|
||
|
// Misc stuff
|
||
|
$this->events['paths'] = array(NULL);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the XML error message
|
||
|
*
|
||
|
* @return string XML Error message
|
||
|
* @access private
|
||
|
*/
|
||
|
function _error()
|
||
|
{
|
||
|
$this->error = "XML error: ".xml_error_string(xml_get_error_code($this->parser))." at line ".xml_get_current_line_number($this->parser);
|
||
|
return $this->error;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Define events for the XML parser
|
||
|
*
|
||
|
* You can define handler functions/objects for start, end, PI and data sections (1.) or
|
||
|
* your can define path which will trigger the defined event when encountered (2.)
|
||
|
*
|
||
|
* Example:
|
||
|
*
|
||
|
* 1.) $parser->setEvents(array("startElement" => "myFunction",
|
||
|
* "endElement" => "myFunction",
|
||
|
* "characterData" => "myFunction",
|
||
|
* "processingInstruction" => "myFunction");
|
||
|
*
|
||
|
* The value can also be an array with the object reference and the method to call.
|
||
|
* i.e. "startElement"=>array(&$myObj, "myMethod") instead of "startelement"=>"myFunction"
|
||
|
*
|
||
|
* 2.) $parser->setEvents(array("/root/foo/bar"=>"myFunction"));
|
||
|
*
|
||
|
* Valid array keys are: 'startElement', 'endElement', 'characterData', 'processingInstruction' and paths
|
||
|
* folowing the scheme '/root/element'. The path MUST begin from the root element and MUST start with '/'.
|
||
|
*
|
||
|
* The value can also be an array with the object reference and the method to call.
|
||
|
* i.e. "/foo/bar"=>array(&$myObj, "myMethod") instead of "/foo/bar"=>"myFunction"
|
||
|
*
|
||
|
* @param array Options array, valid keys are 'startElement', 'endElement', 'characterData', 'processingInstruction', or a path
|
||
|
*
|
||
|
* @access public
|
||
|
* @return void
|
||
|
*/
|
||
|
function setEventHandlers($options = array(NULL))
|
||
|
{
|
||
|
$options = $this->_changeKeyCase($options);
|
||
|
|
||
|
if (array_key_exists('startelement', $options))
|
||
|
{
|
||
|
$this->events['startelement'] = $options['startelement'];
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('endelement', $options))
|
||
|
{
|
||
|
$this->events['endelement'] = $options['endelement'];
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('characterdata', $options))
|
||
|
{
|
||
|
$this->events['characterdata'] = $options['characterdata'];
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('processinginstruction', $options))
|
||
|
{
|
||
|
$this->events['processinginstruction'] = $options['processinginstruction'];
|
||
|
}
|
||
|
|
||
|
$paths = $this->_getDefinedPaths($options);
|
||
|
|
||
|
$this->events['paths'] = $paths;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the processing instruction handler
|
||
|
*
|
||
|
* @param string Processing instruction handler
|
||
|
*
|
||
|
* @return void
|
||
|
* @access private
|
||
|
*/
|
||
|
function _processingInstruction($parser, $target, $data)
|
||
|
{
|
||
|
$handler = $this->_getEventHandler('processinginstruction');
|
||
|
|
||
|
if ($handler)
|
||
|
{
|
||
|
if (is_array($handler))
|
||
|
{
|
||
|
$handler[0]->$handler[1]($target, $data);
|
||
|
} else
|
||
|
{
|
||
|
$handler($target, $data);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Change all array keys to lowercase
|
||
|
* (PHP function change_key_case is available at PHP 4.2 +)
|
||
|
*
|
||
|
* @param array Source array
|
||
|
*
|
||
|
* @return array Array with lowercased keys
|
||
|
* @access private
|
||
|
*/
|
||
|
function _changeKeyCase($options = array())
|
||
|
{
|
||
|
$tmp = array();
|
||
|
|
||
|
foreach ($options as $key => $value)
|
||
|
{
|
||
|
$tmp[strtolower($key)] = $value;
|
||
|
}
|
||
|
|
||
|
return $tmp;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns events handlers if set
|
||
|
*
|
||
|
* @param string Event type
|
||
|
*
|
||
|
* @return sring Event handler name
|
||
|
* @access private
|
||
|
*/
|
||
|
function _getEventHandler($event)
|
||
|
{
|
||
|
// Standard events
|
||
|
if (array_key_exists($event, $this->events))
|
||
|
{
|
||
|
return $this->events[$event];
|
||
|
}
|
||
|
|
||
|
// Paths events
|
||
|
if (array_key_exists($event, $this->events['paths']))
|
||
|
{
|
||
|
return $this->events['paths'][$event];
|
||
|
}
|
||
|
|
||
|
// No events found
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return all defined paths from the options
|
||
|
* array and returns a new array containing
|
||
|
* only the paths to function bindings
|
||
|
*
|
||
|
* @param array Options array
|
||
|
*
|
||
|
* @return array Paths array
|
||
|
* @access private
|
||
|
*/
|
||
|
function _getDefinedPaths($options)
|
||
|
{
|
||
|
$tmp = array();
|
||
|
|
||
|
foreach ($options as $key => $value)
|
||
|
{
|
||
|
if (strstr($key, '/'))
|
||
|
{
|
||
|
$tmp[$key] = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $tmp;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a path to the stack
|
||
|
*
|
||
|
* @param string Element node name
|
||
|
* @access private
|
||
|
* @return void
|
||
|
*/
|
||
|
function _addPath($depth, $name)
|
||
|
{
|
||
|
$this->paths[$depth] = $name;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the current active path
|
||
|
*
|
||
|
* @access private
|
||
|
*/
|
||
|
function _getActivePath()
|
||
|
{
|
||
|
$tmp = array();
|
||
|
|
||
|
for ($i=0; $i<=$this->depth; $i++)
|
||
|
{
|
||
|
$tmp[] = $this->paths[$i];
|
||
|
}
|
||
|
|
||
|
$path = '/' . join('/', $tmp);
|
||
|
return $path;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XML start element handler
|
||
|
*
|
||
|
* @param resource XML Parser resource
|
||
|
* @param string XML Element node name
|
||
|
* @param array XML Element node attributes
|
||
|
*
|
||
|
* @return void
|
||
|
* @access private
|
||
|
*/
|
||
|
function _startElement($parser, $name, $attribs)
|
||
|
{
|
||
|
// Increase depth
|
||
|
$this->depth ++;
|
||
|
|
||
|
// Set active node
|
||
|
$this->activenode = $name;
|
||
|
|
||
|
// Increase element counter
|
||
|
if ($this->activenode == $this->pathdata[$this->activepath][$this->count]['name'])
|
||
|
{
|
||
|
$this->count ++;
|
||
|
} else
|
||
|
{
|
||
|
$this->count = 0;
|
||
|
}
|
||
|
|
||
|
// Entering element context, add subpath
|
||
|
$this->_addPath($this->depth, $name);
|
||
|
|
||
|
// Get the handler for this event
|
||
|
$handler = $this->_getEventHandler('startelement');
|
||
|
|
||
|
// If a handler is defined call it
|
||
|
if ($handler)
|
||
|
{
|
||
|
if (is_array($handler))
|
||
|
{
|
||
|
$handler[0]->$handler[1]($name, $attribs);
|
||
|
} else
|
||
|
{
|
||
|
$handler($name, $attribs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for defined path handlers
|
||
|
$this->activepath = $this->_getActivePath();
|
||
|
|
||
|
// Save path data
|
||
|
$this->pathdata[$this->activepath][$this->count] = array('name'=>$name, 'attribs'=>$attribs);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XML character data handler
|
||
|
*
|
||
|
* @param resource XML Parser resource
|
||
|
* @param string XML node data
|
||
|
*
|
||
|
* @return void
|
||
|
* @access private
|
||
|
*/
|
||
|
function _characterData($parser, $data)
|
||
|
{
|
||
|
// Reset node count
|
||
|
if ($this->activenode != $this->pathdata[$this->activepath][$this->count]['name'])
|
||
|
{
|
||
|
$this->count = 0;
|
||
|
}
|
||
|
|
||
|
// Save path data
|
||
|
$this->pathdata[$this->activepath][$this->count]['content'] .= $data;
|
||
|
|
||
|
// Get the handler for this event
|
||
|
$handler = $this->_getEventHandler('characterdata');
|
||
|
|
||
|
// If a handler is defined call it
|
||
|
if ($handler)
|
||
|
{
|
||
|
if (is_array($handler))
|
||
|
{
|
||
|
$handler[0]->$handler[1]($data);
|
||
|
} else
|
||
|
{
|
||
|
$handler($data);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XML end element handler
|
||
|
*
|
||
|
* @param resource XML Parser resource
|
||
|
* @param string XML Element node name
|
||
|
*
|
||
|
* @return void
|
||
|
* @access private
|
||
|
*/
|
||
|
function _endElement($parser, $name)
|
||
|
{
|
||
|
// Get the handler for this event
|
||
|
$handler = $this->_getEventHandler('endelement');
|
||
|
|
||
|
// Call Element handler
|
||
|
if ($handler)
|
||
|
{
|
||
|
if (is_array($handler))
|
||
|
{
|
||
|
$handler[0]->$handler[1]($name);
|
||
|
} else
|
||
|
{
|
||
|
$handler($name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Reset the active path
|
||
|
$this->activepath = $this->_getActivePath();
|
||
|
|
||
|
// Get handler for the active path
|
||
|
$handler = $this->_getEventHandler($this->activepath);
|
||
|
|
||
|
// Call path handler
|
||
|
if ($handler)
|
||
|
{
|
||
|
if (is_array($handler))
|
||
|
{ // Handler is an object method
|
||
|
$handler[0]->$handler[1]($this->pathdata[$this->activepath][$this->count]['name'],
|
||
|
$this->pathdata[$this->activepath][$this->count]['attribs'],
|
||
|
$this->pathdata[$this->activepath][$this->count]['content']);
|
||
|
} else
|
||
|
{ // Handler is a function
|
||
|
$handler($this->pathdata[$this->activepath][$this->count]['name'],
|
||
|
$this->pathdata[$this->activepath][$this->count]['attribs'],
|
||
|
$this->pathdata[$this->activepath][$this->count]['content']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Decrease depth
|
||
|
$this->depth --;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse a XML string
|
||
|
*
|
||
|
* @param string XML data
|
||
|
*
|
||
|
* @return bool
|
||
|
* @access public
|
||
|
*/
|
||
|
function parse($data, $final = false)
|
||
|
{
|
||
|
$success = xml_parse($this->parser, trim($data), $final);
|
||
|
|
||
|
if ($final && $this->autofree)
|
||
|
{
|
||
|
xml_parser_free($this->parser);
|
||
|
}
|
||
|
|
||
|
if (!$success)
|
||
|
{
|
||
|
return $this->_error();
|
||
|
}
|
||
|
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse a XML file
|
||
|
*
|
||
|
* @param string File location
|
||
|
*
|
||
|
* @return bool
|
||
|
* @access public
|
||
|
*/
|
||
|
function parseFile($file)
|
||
|
{
|
||
|
if (!($fp = fopen($file, "rb")))
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
while ($sData = fread($fp, 4096))
|
||
|
{
|
||
|
if (!xml_parse($this->parser, $sData, feof($fp)))
|
||
|
{
|
||
|
$this->_error();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($this->autofree)
|
||
|
{
|
||
|
xml_parser_free($this->parser);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // XML_Parser
|
||
|
|
||
|
?>
|