<?php
/**
 * dom_xmlrpc_parser is the base parsing class
 * @package   dom-xmlrpc
 * @copyright (C) 2004 John Heinstein. All rights reserved
 * @license   http://www.gnu.org/copyleft/lesser.html LGPL License
 * @author    John Heinstein <johnkarl@nbnet.nb.ca>
 * @link      http://www.engageinteractive.com/dom_xmlrpc/ DOM XML-RPC Home Page
 *            DOM XML-RPC is Free Software
 **/

if (!defined('DOM_XMLRPC_INCLUDE_PATH'))
{
	define('DOM_XMLRPC_INCLUDE_PATH', (dirname(__FILE__) . "/"));
}

require_once(DOM_XMLRPC_INCLUDE_PATH . 'dom_xmlrpc_constants.php');

/**
 * The base parsing class
 *
 * @package dom-xmlrpc
 * @author  John Heinstein <johnkarl@nbnet.nb.ca>
 */
class dom_xmlrpc_parser
{
	/** @var object A wrapper for methodCall and methodResponse arrays */
	var $arrayDocument = null;
	/** @var string A temporary container for parsed string data */
	var $charContainer = '';
	/** @var array A stack holding an hierarchical list of arraytypes (i.e., 'array' or 'struct' or '__phpobject__') */
	var $lastArrayType = array();
	/** @var array A stack holding an hierarchical sequence of array references */
	var $lastArray = array();
	/** @var array A stack holding an hierarchical sequence of struct names */
	var $lastStructName = array();

	/**
	 * Parses the supplied XML text
	 *
	 * @param string  The XML text
	 * @param boolean True if SAXY is to be used
	 *
	 * @return boolean True if the parsing was successful
	 */
	function parseXML($xmlText, $useSAXY = true)
	{
		$xmlText = trim($xmlText);

		require_once(DOM_XMLRPC_INCLUDE_PATH . 'dom_xmlrpc_array_document.php');
		$this->arrayDocument = new dom_xmlrpc_array_document();

		if ($xmlText != '')
		{
			if ($useSAXY || (!function_exists('xml_parser_create')))
			{
				//use SAXY parser to generate array
				return $this->parseSAXY($xmlText);
			}
			else
			{
				//use Expat parser to generate array
				return $this->parse($xmlText);
			}
		}

		return false;
	} //parseXML

	/**
	 * Invokes Expat to parse the XML text
	 *
	 * @param string The XML text
	 *
	 * @return boolean True if the parsing was successful
	 */
	function parse($xmlText)
	{
		//create instance of expat parser (should be included in php distro)
		$parser = xml_parser_create();

		//set handlers for SAX events
		xml_set_object($parser, $this);
		xml_set_element_handler($parser, 'startElement', 'endElement');
		xml_set_character_data_handler($parser, array(&$this, 'dataElement'));
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
		xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);

		//parse out whitespace -  (XML_OPTION_SKIP_WHITE = 1 does not
		//seem to work consistently across versions of PHP and Expat
		$xmlText = eregi_replace('>' . "[[:space:]]+" . '<', '><', $xmlText);

		$success = xml_parse($parser, $xmlText);
		xml_parser_free($parser);

		return $success;
	} //parse

	/**
	 * Invokes SAXY to parse the XML text
	 *
	 * @param string The XML text
	 *
	 * @return boolean True if the parsing was successful
	 */
	function parseSAXY($xmlText)
	{
		//create instance of SAXY parser
		require_once(DOM_XMLRPC_INCLUDE_PATH . 'xml_saxy_parser.php');
		$parser = new SAXY_Lite_Parser();

		$parser->xml_set_element_handler(array(&$this, 'startElement'), array(&$this, 'endElement'));
		$parser->xml_set_character_data_handler(array(&$this, 'dataElement'));

		$success = $parser->parse($xmlText);

		return $success;
	} //parseSAXY

	/**
	 * Returns a reference to the array document
	 * @return array A reference to the array document
	 */
	function &getArrayDocument()
	{
		return $this->arrayDocument;
	} //getArrayDocument

	/**
	 * Abstract method for handling start element events
	 *
	 * @param object A reference to the SAX parser
	 * @param string The name of the start element tag
	 * @param array  An array of attributes (never used by XML-RPC spec)
	 */
	function startElement($parser, $name, $attrs)
	{
		//must override
	} //startElement

	/**
	 * Abstract method for handling end element events
	 *
	 * @param object A reference to the SAX parser
	 * @param string The name of the end element tag
	 */
	function endElement($parser, $name)
	{
		//must override
	} //endElement

	/**
	 * Abstract method for adding an XML-RPC value to the results array
	 *
	 * @param mixed The value
	 */
	function addValue($value)
	{
		//must override
	} //addValue

	/**
	 * Abstract method for handling character data events
	 *
	 * @param object A reference to the SAX parser
	 * @param string The character data
	 */
	function dataElement($parser, $data)
	{
		$this->charContainer .= $data;
	} //dataElement
} //dom_xmlrpc_parser

