<?php
/*

this is a class that loads a wsdl file and makes it's data available to an application
it should provide methods that allow both client and server usage of it

also should have methods for creating a wsdl file from scratch and
serializing wsdl into valid markup

*/

class wsdl
{
	// constructor
	function wsdl($wsdl=false)
	{	
		// define internal arrays of bindings, ports, operations, messages, etc.
		$this->complexTypes = array();
		$this->messages = array();
		$this->currentMessage;
		$this->portOperations = array();
		$this->currentOperation;
		$this->portTypes = array();
		$this->currentPortType;
		$this->bindings = array();
		$this->currentBinding;
		// debug switch
		$this->debug_flag = false;
		// parser vars
		$this->parser;
		$this->position;
		$this->depth;
		$this->depth_array = array();

		if($wsdl == "-1")
		{
			$wsdl=false;
		}
		// Check whether content has been read.
        if($wsdl)
		{
			$wsdl_string = join("",file($wsdl));
            // Create an XML parser.
            $this->parser = xml_parser_create();
            // Set the options for parsing the XML data.
            //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); 
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
            // Set the object for the parser.
            xml_set_object($this->parser, &$this);
            // Set the element handlers for the parser.
            xml_set_element_handler($this->parser, "start_element","end_element");
            xml_set_character_data_handler($this->parser,"character_data");
			//xml_set_default_handler($this->parser, "default_handler");
            
            // Parse the XML file.
            if(!xml_parse($this->parser,$wsdl_string,true))
			{
                // Display an error message.
                $this->debug(sprintf("XML error on line %d: %s",
                	xml_get_current_line_number($this->parser),
                	xml_error_string(xml_get_error_code($this->parser))));
				$this->fault = true;
            }
			xml_parser_free($this->parser);
        }
	}

	// start-element handler
	function start_element($parser, $name, $attrs)
	{
		// position in the total number of elements, starting from 0
		$pos = $this->position++;
		$depth = $this->depth++;
		// set self as current value for this depth
		$this->depth_array[$depth] = $pos;
		
		// find status, register data
		switch($this->status)
		{
			case "types":
				switch($name)
				{
					case "schema":
					$this->schema = true;
					break;
					case "complexType":
						$this->currentElement = $attrs["name"];
						$this->schemaStatus = "complexType";
					break;
					case "element":
						$this->complexTypes[$this->currentElement]["elements"][$attrs["name"]] = $attrs;
					break;
					case "complexContent":
						
					break;
					case "restriction":
						$this->complexTypes[$this->currentElement]["restrictionBase"] = $attrs["base"];
					break;
					case "sequence":
						$this->complexTypes[$this->currentElement]["order"] = "sequence";
					break;
					case "all":
						$this->complexTypes[$this->currentElement]["order"] = "all";
					break;
					case "attribute":
						if($attrs["ref"]){
							$this->complexTypes[$this->currentElement]["attrs"][$attrs["ref"]] = $attrs;
						} elseif($attrs["name"]){
							$this->complexTypes[$this->currentElement]["attrs"][$attrs["name"]] = $attrs;
						}
					break;
				}
			break;
			case "message":
				if($name == "part")
				{
					$this->messages[$this->currentMessage][$attrs["name"]] = $attrs["type"];
				}
			break;
			case "portType":
				switch($name)
				{
					case "operation":
						$this->currentOperation = $attrs["name"];
						$this->portTypes[$this->currentPortType][$attrs["name"]] = $attrs["parameterOrder"];
					break;
					default:
						$this->portOperations[$this->currentOperation][$name]= $attrs;
					break;
				}
			break;
			case "binding":
				switch($name)
				{
					case "soap:binding":
						$this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding],$attrs);
					break;
					case "operation":
						$this->currentOperation = $attrs["name"];
						$this->bindings[$this->currentBinding]["operations"][$attrs["name"]] = array();
					break;
					case "soap:operation":
						$this->bindings[$this->currentBinding]["operations"][$this->currentOperation]["soapAction"] = $attrs["soapAction"];
					break;
					case "input":
						$this->opStatus = "input";
					case "soap:body":
						$this->bindings[$this->currentBinding]["operations"][$this->currentOperation][$this->opStatus] = $attrs;
					break;
					case "output":
						$this->opStatus = "output";
					break;
				}
			break;
			case "service":
				switch($name)
				{
					case "port":
						$this->currentPort = $attrs["name"];
						$this->ports[$attrs["name"]] = $attrs;
					break;
					case "soap:address":
						$this->ports[$this->currentPort]["location"] = $attrs["location"];
					break;
				}
			break;
		}
		// set status
		switch($name)
		{
			case "types":
				$this->status = "types";
			break;
			case "message":
				$this->status = "message";
				$this->messages[$attrs["name"]] = array();
				$this->currentMessage = $attrs["name"];
			break;
			case "portType":
				$this->status = "portType";
				$this->portTypes[$attrs["name"]] = array();
				$this->currentPortType = $attrs["name"];
			break;
			case "binding":
				$this->status = "binding";
				$this->currentBinding = $attrs["name"];
				$this->bindings[$attrs["name"]]["type"] = $attrs["type"];
			break;
			case "service":
				$this->status = "service";
			break;
		}
		// get element prefix
		if(ereg(":",$name))
		{
			$prefix = substr($name,0,strpos($name,":"));
		}
	}

	function getEndpoint($portName)
	{
		if($endpoint = $this->ports[$portName]["location"])
		{
			return $endpoint;
		}
		return false;
	}

	function getPortName($operation)
	{
		@reset($this->ports);
		while(list($port,$portAttrs) = @each($this->ports));
		/* foreach($this->ports as $port => $portAttrs) */
		{
			$binding = substr($portAttrs["binding"],4);
			@reset($this->bindings[$binding]["operations"]);
			while(list($op,$opAttrs) = @each($this->bindings[$binding]["operations"]))
			/* foreach($this->bindings[$binding]["operations"] as $op => $opAttrs) */
			{
				if($op == $operation)
				{
					return $port;
				}
			}
		}
	}

	function getSoapAction($portName,$operation)
	{
		if($binding = substr($this->ports[$portName]["binding"],4))
		{
			if($soapAction = $this->bindings[$binding]["operations"][$operation]["soapAction"])
			{
				return $soapAction;
			}
			return false;
		}
		return false;
	}

	function getNamespace($portName,$operation)
	{
		if($binding = substr($this->ports[$portName]["binding"],4))
		{
			//$this->debug("looking for namespace using binding '$binding', port '$portName', operation '$operation'");
			if($namespace = $this->bindings[$binding]["operations"][$operation]["input"]["namespace"])
			{
				return $namespace;
			}
			return false;
		}
		return false;
	}

	// end-element handler
	function end_element($parser, $name)
	{
		// position of current element is equal to the last value left in depth_array for my depth
		$pos = $this->depth_array[$this->depth];
		// bring depth down a notch
		$this->depth--;	
	}

	// element content handler
	function character_data($parser, $data)
	{
		$pos = $this->depth_array[$this->depth];
		$this->message[$pos]["cdata"] .= $data;
	}

	function debug($string)
	{
		if($this->debug_flag)
		{
			$this->debug_str .= "$string\n";
		}
	}
}
?>