<?php
/**
* dom_xmlrpc_object_parser handles SAX events to convert a DOM XML-RPC XML string into a PHP array
*
* Will generate PHP objects instead of structs if directed
* @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_parser.php');

/**
* Handles SAX events to convert a DOM XML-RPC XML string into a PHP array
*
* Will generate PHP objects instead of structs if directed
* @package dom-xmlrpc
* @author John Heinstein <johnkarl@nbnet.nb.ca>
*/
class dom_xmlrpc_object_parser extends dom_xmlrpc_parser {
	/** @var boolean If true, objects will be tested for */
	var $testingForObject = false; //when a struct is encountered, turn this on	
	/** @var object Handler for PHP objects */
	var $objectDefinitionHandler = null; //fired when a __phpobject__ definition is required
	
	/**
	* Constructor
	* @param object Reference to a handler for PHP objects
	*/
	function dom_xmlrpc_object_parser(&$objectDefinitionHandler) {
		$this->objectDefinitionHandler =& $objectDefinitionHandler;
	} //dom_xmlrpc_object_parser
	
	/**
	* Handles 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) {
		switch($name) {
			case DOM_XMLRPC_TYPE_METHODCALL:
			case DOM_XMLRPC_TYPE_METHODRESPONSE:
			case DOM_XMLRPC_TYPE_FAULT:
				$this->arrayDocument->methodType = $name; //register methodType
				break;
			case DOM_XMLRPC_TYPE_ARRAY: 
			case DOM_XMLRPC_TYPE_STRUCT:
				$this->lastArrayType[] = $name; 
				$this->lastArray[] = array();
				$this->testingForObject = true; 
				break;
		}
	} //startElement
	
	/**
	* Handles end element events
	* @param object A reference to the SAX parser
	* @param string The name of the end element tag
	*/
	function endElement($parser, $name) {
		switch($name) {
			case DOM_XMLRPC_TYPE_STRING:
				//$this->addValue(html_entity_decode($this->charContainer, ENT_QUOTES));
				$this->addValue($this->charContainer);
				break;
			case DOM_XMLRPC_TYPE_I4:
			case DOM_XMLRPC_TYPE_INT:
				$this->addValue((int)($this->charContainer));
				break;
			case DOM_XMLRPC_TYPE_DOUBLE:
				$this->addValue(floatval($this->charContainer));
				break;
			case DOM_XMLRPC_TYPE_BOOLEAN:
				$this->addValue((bool)(trim($this->charContainer)));
				break;
			case DOM_XMLRPC_TYPE_BASE64:
				require_once(DOM_XMLRPC_INCLUDE_PATH . 'dom_xmlrpc_base64.php');
				$base64 =& new dom_xmlrpc_base64();
				$base64->fromString($this->charContainer);
				$this->addValue($base64); //should I add object or string?
				break;
			case DOM_XMLRPC_TYPE_DATETIME:
				require_once(DOM_XMLRPC_INCLUDE_PATH . 'dom_xmlrpc_datetime_iso8601.php');
				$dateTime =& new dom_xmlrpc_datetime_iso8601($this->charContainer);
				$this->addValue($dateTime); //should I add object or string?
				break;
			case DOM_XMLRPC_TYPE_VALUE:
				//if charContainer has anything in it,
				//then there mustn't be a subnode, therefore a <string>
				$myValue = trim($this->charContainer);
				//if ($myValue != '') $this->addValue(html_entity_decode($myValue, ENT_QUOTES));
				if ($myValue != '') $this->addValue($myValue);
				break;
			case DOM_XMLRPC_TYPE_ARRAY:
			case DOM_XMLRPC_TYPE_STRUCT:
				$value =& array_pop($this->lastArray);
				$this->addValue($value);
				array_pop($this->lastArrayType);
				break;
			case DOM_XMLRPC_TYPE_MEMBER:
				array_pop($this->lastStructName);
				break;
			case DOM_XMLRPC_TYPE_NAME:
				$cn = trim($this->charContainer);	
				$this->lastStructName[] = $cn;
				$this->charContainer = '';	
							
				if ($this->testingForObject && ($cn == DOM_XMLRPC_PHPOBJECT)) {
			    	$this->lastArrayType[(count($this->lastArray) - 1)] = DOM_XMLRPC_PHPOBJECT;	
				}	
				
				$this->testingForObject = false;	
				break;
			case DOM_XMLRPC_TYPE_METHODNAME:
				$this->arrayDocument->methodName = trim($this->charContainer);
				$this->charContainer = '';
				break;
		}
	}    //endElement
	
	/**
	* Adds an XML-RPC value to the results array
	* @param mixed The value
	*/
	function addValue($value) {
		$upper = count($this->lastArray) - 1;

		if ($upper > -1) {
			$lastArrayType = $this->lastArrayType[$upper];
			
			if ($lastArrayType == DOM_XMLRPC_TYPE_STRUCT) {
				$currentName = $this->lastStructName[(count($this->lastStructName) - 1)];

				switch ($currentName) {
					case DOM_XMLRPC_NODEVALUE_FAULTCODE:
						$this->arrayDocument->faultCode = $value;
						break;
						
					case DOM_XMLRPC_NODEVALUE_FAULTSTRING:
						$this->arrayDocument->faultString = $value;
						break;
						
					default:	
						//associative array item					
						$this->lastArray[$upper][$currentName] = $value; 
				}
			} 
			else if ($lastArrayType == DOM_XMLRPC_PHPOBJECT) {
				$currentName = $this->lastStructName[(count($this->lastStructName) - 1)];
				
				if ($currentName == DOM_XMLRPC_PHPOBJECT) {
					//import class and instantiate new object
					call_user_func($this->objectDefinitionHandler, $value);
			    	$this->lastArray[$upper] =& new $value;
				}
				else {
					if ($currentName == DOM_XMLRPC_SERIALIZED) {
						//could also check for this...
						//if (is_object($value) && (get_class($value) == 'dom_xmlrpc_base64')) {
						//unserialize object
						$serialized =& $value->getBinary();
					    $this->lastArray[$upper] =& unserialize($serialized);
					}
					else {
						//add property to object
						$myObj =& $this->lastArray[$upper];
		    			$myObj->$currentName =& $value;
					}
				} 
			}
			else {
				//indexed array item
				$this->lastArray[$upper][] =& $value; 
			} 
		}
		else {
			//at root level, add value as a new param
			array_push($this->arrayDocument->params,  $value); 
		}
		
		$this->charContainer = '';
	} //addValue   
} //dom_xmlrpc_object_parser

?>