shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

class.wsdl.php (78338B)


      1 <?php
      2 
      3 
      4 
      5 
      6 /**
      7 * parses a WSDL file, allows access to it's data, other utility methods.
      8 * also builds WSDL structures programmatically.
      9 * 
     10 * @author   Dietrich Ayala <dietrich@ganx4.com>
     11 * @author   Scott Nichol <snichol@users.sourceforge.net>
     12 * @version  $Id: class.wsdl.php,v 1.76 2010/04/26 20:15:08 snichol Exp $
     13 * @access public 
     14 */
     15 class wsdl extends nusoap_base {
     16 	// URL or filename of the root of this WSDL
     17     var $wsdl; 
     18     // define internal arrays of bindings, ports, operations, messages, etc.
     19     var $schemas = array();
     20     var $currentSchema;
     21     var $message = array();
     22     var $complexTypes = array();
     23     var $messages = array();
     24     var $currentMessage;
     25     var $currentOperation;
     26     var $portTypes = array();
     27     var $currentPortType;
     28     var $bindings = array();
     29     var $currentBinding;
     30     var $ports = array();
     31     var $currentPort;
     32     var $opData = array();
     33     var $status = '';
     34     var $documentation = false;
     35     var $endpoint = ''; 
     36     // array of wsdl docs to import
     37     var $import = array(); 
     38     // parser vars
     39     var $parser;
     40     var $position = 0;
     41     var $depth = 0;
     42     var $depth_array = array();
     43 	// for getting wsdl
     44 	var $proxyhost = '';
     45     var $proxyport = '';
     46 	var $proxyusername = '';
     47 	var $proxypassword = '';
     48 	var $timeout = 0;
     49 	var $response_timeout = 30;
     50 	var $curl_options = array();	// User-specified cURL options
     51 	var $use_curl = false;			// whether to always try to use cURL
     52 	// for HTTP authentication
     53 	var $username = '';				// Username for HTTP authentication
     54 	var $password = '';				// Password for HTTP authentication
     55 	var $authtype = '';				// Type of HTTP authentication
     56 	var $certRequest = array();		// Certificate for HTTP SSL authentication
     57 
     58     /**
     59      * constructor
     60      * 
     61      * @param string $wsdl WSDL document URL
     62 	 * @param string $proxyhost
     63 	 * @param string $proxyport
     64 	 * @param string $proxyusername
     65 	 * @param string $proxypassword
     66 	 * @param integer $timeout set the connection timeout
     67 	 * @param integer $response_timeout set the response timeout
     68 	 * @param array $curl_options user-specified cURL options
     69 	 * @param boolean $use_curl try to use cURL
     70      * @access public 
     71      */
     72     function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
     73 		parent::nusoap_base();
     74 		$this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
     75         $this->proxyhost = $proxyhost;
     76         $this->proxyport = $proxyport;
     77 		$this->proxyusername = $proxyusername;
     78 		$this->proxypassword = $proxypassword;
     79 		$this->timeout = $timeout;
     80 		$this->response_timeout = $response_timeout;
     81 		if (is_array($curl_options))
     82 			$this->curl_options = $curl_options;
     83 		$this->use_curl = $use_curl;
     84 		$this->fetchWSDL($wsdl);
     85     }
     86 
     87 	/**
     88 	 * fetches the WSDL document and parses it
     89 	 *
     90 	 * @access public
     91 	 */
     92 	function fetchWSDL($wsdl) {
     93 		$this->debug("parse and process WSDL path=$wsdl");
     94 		$this->wsdl = $wsdl;
     95         // parse wsdl file
     96         if ($this->wsdl != "") {
     97             $this->parseWSDL($this->wsdl);
     98         }
     99         // imports
    100         // TODO: handle imports more properly, grabbing them in-line and nesting them
    101     	$imported_urls = array();
    102     	$imported = 1;
    103     	while ($imported > 0) {
    104     		$imported = 0;
    105     		// Schema imports
    106     		foreach ($this->schemas as $ns => $list) {
    107     			foreach ($list as $xs) {
    108 					$wsdlparts = parse_url($this->wsdl);	// this is bogusly simple!
    109 		            foreach ($xs->imports as $ns2 => $list2) {
    110 		                for ($ii = 0; $ii < count($list2); $ii++) {
    111 		                	if (! $list2[$ii]['loaded']) {
    112 		                		$this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
    113 		                		$url = $list2[$ii]['location'];
    114 								if ($url != '') {
    115 									$urlparts = parse_url($url);
    116 									if (!isset($urlparts['host'])) {
    117 										$url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
    118 												substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
    119 									}
    120 									if (! in_array($url, $imported_urls)) {
    121 					                	$this->parseWSDL($url);
    122 				                		$imported++;
    123 				                		$imported_urls[] = $url;
    124 				                	}
    125 								} else {
    126 									$this->debug("Unexpected scenario: empty URL for unloaded import");
    127 								}
    128 							}
    129 						}
    130 		            } 
    131     			}
    132     		}
    133     		// WSDL imports
    134 			$wsdlparts = parse_url($this->wsdl);	// this is bogusly simple!
    135             foreach ($this->import as $ns => $list) {
    136                 for ($ii = 0; $ii < count($list); $ii++) {
    137                 	if (! $list[$ii]['loaded']) {
    138                 		$this->import[$ns][$ii]['loaded'] = true;
    139                 		$url = $list[$ii]['location'];
    140 						if ($url != '') {
    141 							$urlparts = parse_url($url);
    142 							if (!isset($urlparts['host'])) {
    143 								$url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
    144 										substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
    145 							}
    146 							if (! in_array($url, $imported_urls)) {
    147 			                	$this->parseWSDL($url);
    148 		                		$imported++;
    149 		                		$imported_urls[] = $url;
    150 		                	}
    151 						} else {
    152 							$this->debug("Unexpected scenario: empty URL for unloaded import");
    153 						}
    154 					}
    155 				}
    156             } 
    157 		}
    158         // add new data to operation data
    159         foreach($this->bindings as $binding => $bindingData) {
    160             if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
    161                 foreach($bindingData['operations'] as $operation => $data) {
    162                     $this->debug('post-parse data gathering for ' . $operation);
    163                     $this->bindings[$binding]['operations'][$operation]['input'] = 
    164 						isset($this->bindings[$binding]['operations'][$operation]['input']) ? 
    165 						array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
    166 						$this->portTypes[ $bindingData['portType'] ][$operation]['input'];
    167                     $this->bindings[$binding]['operations'][$operation]['output'] = 
    168 						isset($this->bindings[$binding]['operations'][$operation]['output']) ?
    169 						array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
    170 						$this->portTypes[ $bindingData['portType'] ][$operation]['output'];
    171                     if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
    172 						$this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
    173 					}
    174 					if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
    175                    		$this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
    176                     }
    177                     // Set operation style if necessary, but do not override one already provided
    178 					if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
    179                         $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
    180                     }
    181                     $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
    182                     $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
    183                     $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
    184                 } 
    185             } 
    186         }
    187 	}
    188 
    189     /**
    190      * parses the wsdl document
    191      * 
    192      * @param string $wsdl path or URL
    193      * @access private 
    194      */
    195     function parseWSDL($wsdl = '') {
    196 		$this->debug("parse WSDL at path=$wsdl");
    197 
    198         if ($wsdl == '') {
    199             $this->debug('no wsdl passed to parseWSDL()!!');
    200             $this->setError('no wsdl passed to parseWSDL()!!');
    201             return false;
    202         }
    203         
    204         // parse $wsdl for url format
    205         $wsdl_props = parse_url($wsdl);
    206 
    207         if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
    208             $this->debug('getting WSDL http(s) URL ' . $wsdl);
    209         	// get wsdl
    210 	        $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
    211 			$tr->request_method = 'GET';
    212 			$tr->useSOAPAction = false;
    213 			if($this->proxyhost && $this->proxyport){
    214 				$tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
    215 			}
    216 			if ($this->authtype != '') {
    217 				$tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
    218 			}
    219 			$tr->setEncoding('gzip, deflate');
    220 			$wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
    221 			//$this->debug("WSDL request\n" . $tr->outgoing_payload);
    222 			//$this->debug("WSDL response\n" . $tr->incoming_payload);
    223 			$this->appendDebug($tr->getDebug());
    224 			// catch errors
    225 			if($err = $tr->getError() ){
    226 				$errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err;
    227 				$this->debug($errstr);
    228 	            $this->setError($errstr);
    229 				unset($tr);
    230 	            return false;
    231 			}
    232 			unset($tr);
    233 			$this->debug("got WSDL URL");
    234         } else {
    235             // $wsdl is not http(s), so treat it as a file URL or plain file path
    236         	if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
    237         		$path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
    238         	} else {
    239         		$path = $wsdl;
    240         	}
    241             $this->debug('getting WSDL file ' . $path);
    242             if ($fp = @fopen($path, 'r')) {
    243                 $wsdl_string = '';
    244                 while ($data = fread($fp, 32768)) {
    245                     $wsdl_string .= $data;
    246                 } 
    247                 fclose($fp);
    248             } else {
    249             	$errstr = "Bad path to WSDL file $path";
    250             	$this->debug($errstr);
    251                 $this->setError($errstr);
    252                 return false;
    253             } 
    254         }
    255         $this->debug('Parse WSDL');
    256         // end new code added
    257         // Create an XML parser.
    258         $this->parser = xml_parser_create(); 
    259         // Set the options for parsing the XML data.
    260         // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
    261         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
    262         // Set the object for the parser.
    263         xml_set_object($this->parser, $this); 
    264         // Set the element handlers for the parser.
    265         xml_set_element_handler($this->parser, 'start_element', 'end_element');
    266         xml_set_character_data_handler($this->parser, 'character_data');
    267         // Parse the XML file.
    268         if (!xml_parse($this->parser, $wsdl_string, true)) {
    269             // Display an error message.
    270             $errstr = sprintf(
    271 				'XML error parsing WSDL from %s on line %d: %s',
    272 				$wsdl,
    273                 xml_get_current_line_number($this->parser),
    274                 xml_error_string(xml_get_error_code($this->parser))
    275                 );
    276             $this->debug($errstr);
    277 			$this->debug("XML payload:\n" . $wsdl_string);
    278             $this->setError($errstr);
    279             return false;
    280         } 
    281 		// free the parser
    282         xml_parser_free($this->parser);
    283         $this->debug('Parsing WSDL done');
    284 		// catch wsdl parse errors
    285 		if($this->getError()){
    286 			return false;
    287 		}
    288         return true;
    289     } 
    290 
    291     /**
    292      * start-element handler
    293      * 
    294      * @param string $parser XML parser object
    295      * @param string $name element name
    296      * @param string $attrs associative array of attributes
    297      * @access private 
    298      */
    299     function start_element($parser, $name, $attrs)
    300     {
    301         if ($this->status == 'schema') {
    302             $this->currentSchema->schemaStartElement($parser, $name, $attrs);
    303             $this->appendDebug($this->currentSchema->getDebug());
    304             $this->currentSchema->clearDebug();
    305         } elseif (preg_match('/schema$/', $name)) {
    306         	$this->debug('Parsing WSDL schema');
    307             // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
    308             $this->status = 'schema';
    309             $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
    310             $this->currentSchema->schemaStartElement($parser, $name, $attrs);
    311             $this->appendDebug($this->currentSchema->getDebug());
    312             $this->currentSchema->clearDebug();
    313         } else {
    314             // position in the total number of elements, starting from 0
    315             $pos = $this->position++;
    316             $depth = $this->depth++; 
    317             // set self as current value for this depth
    318             $this->depth_array[$depth] = $pos;
    319             $this->message[$pos] = array('cdata' => ''); 
    320             // process attributes
    321             if (count($attrs) > 0) {
    322 				// register namespace declarations
    323                 foreach($attrs as $k => $v) {
    324                     if (preg_match('/^xmlns/',$k)) {
    325                         if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
    326                             $this->namespaces[$ns_prefix] = $v;
    327                         } else {
    328                             $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
    329                         } 
    330                         if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
    331                             $this->XMLSchemaVersion = $v;
    332                             $this->namespaces['xsi'] = $v . '-instance';
    333                         } 
    334                     }
    335                 }
    336                 // expand each attribute prefix to its namespace
    337                 foreach($attrs as $k => $v) {
    338                     $k = strpos($k, ':') ? $this->expandQname($k) : $k;
    339                     if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
    340                         $v = strpos($v, ':') ? $this->expandQname($v) : $v;
    341                     } 
    342                     $eAttrs[$k] = $v;
    343                 } 
    344                 $attrs = $eAttrs;
    345             } else {
    346                 $attrs = array();
    347             } 
    348             // get element prefix, namespace and name
    349             if (preg_match('/:/', $name)) {
    350                 // get ns prefix
    351                 $prefix = substr($name, 0, strpos($name, ':')); 
    352                 // get ns
    353                 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; 
    354                 // get unqualified name
    355                 $name = substr(strstr($name, ':'), 1);
    356             } 
    357 			// process attributes, expanding any prefixes to namespaces
    358             // find status, register data
    359             switch ($this->status) {
    360                 case 'message':
    361                     if ($name == 'part') {
    362 			            if (isset($attrs['type'])) {
    363 		                    $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
    364 		                    $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
    365             			} 
    366 			            if (isset($attrs['element'])) {
    367 		                    $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
    368 			                $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
    369 			            } 
    370         			} 
    371         			break;
    372 			    case 'portType':
    373 			        switch ($name) {
    374 			            case 'operation':
    375 			                $this->currentPortOperation = $attrs['name'];
    376 			                $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
    377 			                if (isset($attrs['parameterOrder'])) {
    378 			                	$this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
    379 			        		} 
    380 			        		break;
    381 					    case 'documentation':
    382 					        $this->documentation = true;
    383 					        break; 
    384 					    // merge input/output data
    385 					    default:
    386 					        $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
    387 					        $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
    388 					        break;
    389 					} 
    390 			    	break;
    391 				case 'binding':
    392 				    switch ($name) {
    393 				        case 'binding': 
    394 				            // get ns prefix
    395 				            if (isset($attrs['style'])) {
    396 				            $this->bindings[$this->currentBinding]['prefix'] = $prefix;
    397 					    	} 
    398 					    	$this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
    399 					    	break;
    400 						case 'header':
    401 						    $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
    402 						    break;
    403 						case 'operation':
    404 						    if (isset($attrs['soapAction'])) {
    405 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
    406 						    } 
    407 						    if (isset($attrs['style'])) {
    408 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
    409 						    } 
    410 						    if (isset($attrs['name'])) {
    411 						        $this->currentOperation = $attrs['name'];
    412 						        $this->debug("current binding operation: $this->currentOperation");
    413 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
    414 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
    415 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
    416 						    } 
    417 						    break;
    418 						case 'input':
    419 						    $this->opStatus = 'input';
    420 						    break;
    421 						case 'output':
    422 						    $this->opStatus = 'output';
    423 						    break;
    424 						case 'body':
    425 						    if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
    426 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
    427 						    } else {
    428 						        $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
    429 						    } 
    430 						    break;
    431 					} 
    432 					break;
    433 				case 'service':
    434 					switch ($name) {
    435 					    case 'port':
    436 					        $this->currentPort = $attrs['name'];
    437 					        $this->debug('current port: ' . $this->currentPort);
    438 					        $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
    439 					
    440 					        break;
    441 					    case 'address':
    442 					        $this->ports[$this->currentPort]['location'] = $attrs['location'];
    443 					        $this->ports[$this->currentPort]['bindingType'] = $namespace;
    444 					        $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
    445 					        $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
    446 					        break;
    447 					} 
    448 					break;
    449 			} 
    450 		// set status
    451 		switch ($name) {
    452 			case 'import':
    453 			    if (isset($attrs['location'])) {
    454                     $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
    455                     $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
    456 				} else {
    457                     $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
    458 					if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
    459 						$this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
    460 					}
    461                     $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
    462 				}
    463 				break;
    464 			//wait for schema
    465 			//case 'types':
    466 			//	$this->status = 'schema';
    467 			//	break;
    468 			case 'message':
    469 				$this->status = 'message';
    470 				$this->messages[$attrs['name']] = array();
    471 				$this->currentMessage = $attrs['name'];
    472 				break;
    473 			case 'portType':
    474 				$this->status = 'portType';
    475 				$this->portTypes[$attrs['name']] = array();
    476 				$this->currentPortType = $attrs['name'];
    477 				break;
    478 			case "binding":
    479 				if (isset($attrs['name'])) {
    480 				// get binding name
    481 					if (strpos($attrs['name'], ':')) {
    482 			    		$this->currentBinding = $this->getLocalPart($attrs['name']);
    483 					} else {
    484 			    		$this->currentBinding = $attrs['name'];
    485 					} 
    486 					$this->status = 'binding';
    487 					$this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
    488 					$this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
    489 				} 
    490 				break;
    491 			case 'service':
    492 				$this->serviceName = $attrs['name'];
    493 				$this->status = 'service';
    494 				$this->debug('current service: ' . $this->serviceName);
    495 				break;
    496 			case 'definitions':
    497 				foreach ($attrs as $name => $value) {
    498 					$this->wsdl_info[$name] = $value;
    499 				} 
    500 				break;
    501 			} 
    502 		} 
    503 	} 
    504 
    505 	/**
    506 	* end-element handler
    507 	* 
    508 	* @param string $parser XML parser object
    509 	* @param string $name element name
    510 	* @access private 
    511 	*/
    512 	function end_element($parser, $name){ 
    513 		// unset schema status
    514 		if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) {
    515 			$this->status = "";
    516             $this->appendDebug($this->currentSchema->getDebug());
    517             $this->currentSchema->clearDebug();
    518 			$this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
    519         	$this->debug('Parsing WSDL schema done');
    520 		} 
    521 		if ($this->status == 'schema') {
    522 			$this->currentSchema->schemaEndElement($parser, $name);
    523 		} else {
    524 			// bring depth down a notch
    525 			$this->depth--;
    526 		} 
    527 		// end documentation
    528 		if ($this->documentation) {
    529 			//TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
    530 			//$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
    531 			$this->documentation = false;
    532 		} 
    533 	} 
    534 
    535 	/**
    536 	 * element content handler
    537 	 * 
    538 	 * @param string $parser XML parser object
    539 	 * @param string $data element content
    540 	 * @access private 
    541 	 */
    542 	function character_data($parser, $data)
    543 	{
    544 		$pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
    545 		if (isset($this->message[$pos]['cdata'])) {
    546 			$this->message[$pos]['cdata'] .= $data;
    547 		} 
    548 		if ($this->documentation) {
    549 			$this->documentation .= $data;
    550 		} 
    551 	} 
    552 
    553 	/**
    554 	* if authenticating, set user credentials here
    555 	*
    556 	* @param    string $username
    557 	* @param    string $password
    558 	* @param	string $authtype (basic|digest|certificate|ntlm)
    559 	* @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
    560 	* @access   public
    561 	*/
    562 	function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
    563 		$this->debug("setCredentials username=$username authtype=$authtype certRequest=");
    564 		$this->appendDebug($this->varDump($certRequest));
    565 		$this->username = $username;
    566 		$this->password = $password;
    567 		$this->authtype = $authtype;
    568 		$this->certRequest = $certRequest;
    569 	}
    570 	
    571 	function getBindingData($binding)
    572 	{
    573 		if (is_array($this->bindings[$binding])) {
    574 			return $this->bindings[$binding];
    575 		} 
    576 	}
    577 	
    578 	/**
    579 	 * returns an assoc array of operation names => operation data
    580 	 * 
    581 	 * @param string $portName WSDL port name
    582 	 * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
    583 	 * @return array 
    584 	 * @access public 
    585 	 */
    586 	function getOperations($portName = '', $bindingType = 'soap') {
    587 		$ops = array();
    588 		if ($bindingType == 'soap') {
    589 			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
    590 		} elseif ($bindingType == 'soap12') {
    591 			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
    592 		} else {
    593 			$this->debug("getOperations bindingType $bindingType may not be supported");
    594 		}
    595 		$this->debug("getOperations for port '$portName' bindingType $bindingType");
    596 		// loop thru ports
    597 		foreach($this->ports as $port => $portData) {
    598 			$this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
    599 			if ($portName == '' || $port == $portName) {
    600 				// binding type of port matches parameter
    601 				if ($portData['bindingType'] == $bindingType) {
    602 					$this->debug("getOperations found port $port bindingType $bindingType");
    603 					//$this->debug("port data: " . $this->varDump($portData));
    604 					//$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
    605 					// merge bindings
    606 					if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
    607 						$ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
    608 					}
    609 				}
    610 			}
    611 		}
    612 		if (count($ops) == 0) {
    613 			$this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
    614 		}
    615 		return $ops;
    616 	} 
    617 	
    618 	/**
    619 	 * returns an associative array of data necessary for calling an operation
    620 	 * 
    621 	 * @param string $operation name of operation
    622 	 * @param string $bindingType type of binding eg: soap, soap12
    623 	 * @return array 
    624 	 * @access public 
    625 	 */
    626 	function getOperationData($operation, $bindingType = 'soap')
    627 	{
    628 		if ($bindingType == 'soap') {
    629 			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
    630 		} elseif ($bindingType == 'soap12') {
    631 			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
    632 		}
    633 		// loop thru ports
    634 		foreach($this->ports as $port => $portData) {
    635 			// binding type of port matches parameter
    636 			if ($portData['bindingType'] == $bindingType) {
    637 				// get binding
    638 				//foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
    639 				foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
    640 					// note that we could/should also check the namespace here
    641 					if ($operation == $bOperation) {
    642 						$opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
    643 					    return $opData;
    644 					} 
    645 				} 
    646 			}
    647 		} 
    648 	}
    649 	
    650 	/**
    651 	 * returns an associative array of data necessary for calling an operation
    652 	 * 
    653 	 * @param string $soapAction soapAction for operation
    654 	 * @param string $bindingType type of binding eg: soap, soap12
    655 	 * @return array 
    656 	 * @access public 
    657 	 */
    658 	function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
    659 		if ($bindingType == 'soap') {
    660 			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
    661 		} elseif ($bindingType == 'soap12') {
    662 			$bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
    663 		}
    664 		// loop thru ports
    665 		foreach($this->ports as $port => $portData) {
    666 			// binding type of port matches parameter
    667 			if ($portData['bindingType'] == $bindingType) {
    668 				// loop through operations for the binding
    669 				foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
    670 					if ($opData['soapAction'] == $soapAction) {
    671 					    return $opData;
    672 					} 
    673 				} 
    674 			}
    675 		} 
    676 	}
    677 	
    678 	/**
    679     * returns an array of information about a given type
    680     * returns false if no type exists by the given name
    681     *
    682 	*	 typeDef = array(
    683 	*	 'elements' => array(), // refs to elements array
    684 	*	'restrictionBase' => '',
    685 	*	'phpType' => '',
    686 	*	'order' => '(sequence|all)',
    687 	*	'attrs' => array() // refs to attributes array
    688 	*	)
    689     *
    690     * @param string $type the type
    691     * @param string $ns namespace (not prefix) of the type
    692     * @return mixed
    693     * @access public
    694     * @see nusoap_xmlschema
    695     */
    696 	function getTypeDef($type, $ns) {
    697 		$this->debug("in getTypeDef: type=$type, ns=$ns");
    698 		if ((! $ns) && isset($this->namespaces['tns'])) {
    699 			$ns = $this->namespaces['tns'];
    700 			$this->debug("in getTypeDef: type namespace forced to $ns");
    701 		}
    702 		if (!isset($this->schemas[$ns])) {
    703 			foreach ($this->schemas as $ns0 => $schema0) {
    704 				if (strcasecmp($ns, $ns0) == 0) {
    705 					$this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
    706 					$ns = $ns0;
    707 					break;
    708 				}
    709 			}
    710 		}
    711 		if (isset($this->schemas[$ns])) {
    712 			$this->debug("in getTypeDef: have schema for namespace $ns");
    713 			for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
    714 				$xs = &$this->schemas[$ns][$i];
    715 				$t = $xs->getTypeDef($type);
    716 				$this->appendDebug($xs->getDebug());
    717 				$xs->clearDebug();
    718 				if ($t) {
    719 					$this->debug("in getTypeDef: found type $type");
    720 					if (!isset($t['phpType'])) {
    721 						// get info for type to tack onto the element
    722 						$uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
    723 						$ns = substr($t['type'], 0, strrpos($t['type'], ':'));
    724 						$etype = $this->getTypeDef($uqType, $ns);
    725 						if ($etype) {
    726 							$this->debug("found type for [element] $type:");
    727 							$this->debug($this->varDump($etype));
    728 							if (isset($etype['phpType'])) {
    729 								$t['phpType'] = $etype['phpType'];
    730 							}
    731 							if (isset($etype['elements'])) {
    732 								$t['elements'] = $etype['elements'];
    733 							}
    734 							if (isset($etype['attrs'])) {
    735 								$t['attrs'] = $etype['attrs'];
    736 							}
    737 						} else {
    738 							$this->debug("did not find type for [element] $type");
    739 						}
    740 					}
    741 					return $t;
    742 				}
    743 			}
    744 			$this->debug("in getTypeDef: did not find type $type");
    745 		} else {
    746 			$this->debug("in getTypeDef: do not have schema for namespace $ns");
    747 		}
    748 		return false;
    749 	}
    750 
    751     /**
    752     * prints html description of services
    753     *
    754     * @access private
    755     */
    756     function webDescription(){
    757     	global $HTTP_SERVER_VARS;
    758 
    759 		if (isset($_SERVER)) {
    760 			$PHP_SELF = $_SERVER['PHP_SELF'];
    761 		} elseif (isset($HTTP_SERVER_VARS)) {
    762 			$PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
    763 		} else {
    764 			$this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
    765 		}
    766 
    767 		$b = '
    768 		<html><head><title>NuSOAP: '.$this->serviceName.'</title>
    769 		<style type="text/css">
    770 		    body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
    771 		    p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
    772 		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
    773 		    ul      { margin-top: 10px; margin-left: 20px; }
    774 		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
    775 		    .content{
    776 			margin-left: 0px; padding-bottom: 2em; }
    777 		    .nav {
    778 			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
    779 			margin-top: 10px; margin-left: 0px; color: #000000;
    780 			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
    781 		    .title {
    782 			font-family: arial; font-size: 26px; color: #ffffff;
    783 			background-color: #999999; width: 100%;
    784 			margin-left: 0px; margin-right: 0px;
    785 			padding-top: 10px; padding-bottom: 10px;}
    786 		    .hidden {
    787 			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
    788 			font-family: arial; overflow: hidden; width: 600;
    789 			padding: 20px; font-size: 10px; background-color: #999999;
    790 			layer-background-color:#FFFFFF; }
    791 		    a,a:active  { color: charcoal; font-weight: bold; }
    792 		    a:visited   { color: #666666; font-weight: bold; }
    793 		    a:hover     { color: cc3300; font-weight: bold; }
    794 		</style>
    795 		<script language="JavaScript" type="text/javascript">
    796 		<!--
    797 		// POP-UP CAPTIONS...
    798 		function lib_bwcheck(){ //Browsercheck (needed)
    799 		    this.ver=navigator.appVersion
    800 		    this.agent=navigator.userAgent
    801 		    this.dom=document.getElementById?1:0
    802 		    this.opera5=this.agent.indexOf("Opera 5")>-1
    803 		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
    804 		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
    805 		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
    806 		    this.ie=this.ie4||this.ie5||this.ie6
    807 		    this.mac=this.agent.indexOf("Mac")>-1
    808 		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
    809 		    this.ns4=(document.layers && !this.dom)?1:0;
    810 		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
    811 		    return this
    812 		}
    813 		var bw = new lib_bwcheck()
    814 		//Makes crossbrowser object.
    815 		function makeObj(obj){
    816 		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
    817 		    if(!this.evnt) return false
    818 		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
    819 		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
    820 		    this.writeIt=b_writeIt;
    821 		    return this
    822 		}
    823 		// A unit of measure that will be added when setting the position of a layer.
    824 		//var px = bw.ns4||window.opera?"":"px";
    825 		function b_writeIt(text){
    826 		    if (bw.ns4){this.wref.write(text);this.wref.close()}
    827 		    else this.wref.innerHTML = text
    828 		}
    829 		//Shows the messages
    830 		var oDesc;
    831 		function popup(divid){
    832 		    if(oDesc = new makeObj(divid)){
    833 			oDesc.css.visibility = "visible"
    834 		    }
    835 		}
    836 		function popout(){ // Hides message
    837 		    if(oDesc) oDesc.css.visibility = "hidden"
    838 		}
    839 		//-->
    840 		</script>
    841 		</head>
    842 		<body>
    843 		<div class=content>
    844 			<br><br>
    845 			<div class=title>'.$this->serviceName.'</div>
    846 			<div class=nav>
    847 				<p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
    848 				Click on an operation name to view it&apos;s details.</p>
    849 				<ul>';
    850 				foreach($this->getOperations() as $op => $data){
    851 				    $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
    852 				    // create hidden div
    853 				    $b .= "<div id='$op' class='hidden'>
    854 				    <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
    855 				    foreach($data as $donnie => $marie){ // loop through opdata
    856 						if($donnie == 'input' || $donnie == 'output'){ // show input/output data
    857 						    $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
    858 						    foreach($marie as $captain => $tenille){ // loop through data
    859 								if($captain == 'parts'){ // loop thru parts
    860 								    $b .= "&nbsp;&nbsp;$captain:<br>";
    861 					                //if(is_array($tenille)){
    862 								    	foreach($tenille as $joanie => $chachi){
    863 											$b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
    864 								    	}
    865 					        		//}
    866 								} else {
    867 								    $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
    868 								}
    869 						    }
    870 						} else {
    871 						    $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
    872 						}
    873 				    }
    874 					$b .= '</div>';
    875 				}
    876 				$b .= '
    877 				<ul>
    878 			</div>
    879 		</div></body></html>';
    880 		return $b;
    881     }
    882 
    883 	/**
    884 	* serialize the parsed wsdl
    885 	*
    886 	* @param mixed $debug whether to put debug=1 in endpoint URL
    887 	* @return string serialization of WSDL
    888 	* @access public 
    889 	*/
    890 	function serialize($debug = 0)
    891 	{
    892 		$xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
    893 		$xml .= "\n<definitions";
    894 		foreach($this->namespaces as $k => $v) {
    895 			$xml .= " xmlns:$k=\"$v\"";
    896 		} 
    897 		// 10.9.02 - add poulter fix for wsdl and tns declarations
    898 		if (isset($this->namespaces['wsdl'])) {
    899 			$xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
    900 		} 
    901 		if (isset($this->namespaces['tns'])) {
    902 			$xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
    903 		} 
    904 		$xml .= '>'; 
    905 		// imports
    906 		if (sizeof($this->import) > 0) {
    907 			foreach($this->import as $ns => $list) {
    908 				foreach ($list as $ii) {
    909 					if ($ii['location'] != '') {
    910 						$xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
    911 					} else {
    912 						$xml .= '<import namespace="' . $ns . '" />';
    913 					}
    914 				}
    915 			} 
    916 		} 
    917 		// types
    918 		if (count($this->schemas)>=1) {
    919 			$xml .= "\n<types>\n";
    920 			foreach ($this->schemas as $ns => $list) {
    921 				foreach ($list as $xs) {
    922 					$xml .= $xs->serializeSchema();
    923 				}
    924 			}
    925 			$xml .= '</types>';
    926 		} 
    927 		// messages
    928 		if (count($this->messages) >= 1) {
    929 			foreach($this->messages as $msgName => $msgParts) {
    930 				$xml .= "\n<message name=\"" . $msgName . '">';
    931 				if(is_array($msgParts)){
    932 					foreach($msgParts as $partName => $partType) {
    933 						// print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
    934 						if (strpos($partType, ':')) {
    935 						    $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
    936 						} elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
    937 						    // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
    938 						    $typePrefix = 'xsd';
    939 						} else {
    940 						    foreach($this->typemap as $ns => $types) {
    941 						        if (isset($types[$partType])) {
    942 						            $typePrefix = $this->getPrefixFromNamespace($ns);
    943 						        } 
    944 						    } 
    945 						    if (!isset($typePrefix)) {
    946 						        die("$partType has no namespace!");
    947 						    } 
    948 						}
    949 						$ns = $this->getNamespaceFromPrefix($typePrefix);
    950 						$localPart = $this->getLocalPart($partType);
    951 						$typeDef = $this->getTypeDef($localPart, $ns);
    952 						if ($typeDef['typeClass'] == 'element') {
    953 							$elementortype = 'element';
    954 							if (substr($localPart, -1) == '^') {
    955 								$localPart = substr($localPart, 0, -1);
    956 							}
    957 						} else {
    958 							$elementortype = 'type';
    959 						}
    960 						$xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
    961 					}
    962 				}
    963 				$xml .= '</message>';
    964 			} 
    965 		} 
    966 		// bindings & porttypes
    967 		if (count($this->bindings) >= 1) {
    968 			$binding_xml = '';
    969 			$portType_xml = '';
    970 			foreach($this->bindings as $bindingName => $attrs) {
    971 				$binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
    972 				$binding_xml .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
    973 				$portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
    974 				foreach($attrs['operations'] as $opName => $opParts) {
    975 					$binding_xml .= "\n" . '  <operation name="' . $opName . '">';
    976 					$binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
    977 					if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
    978 						$enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
    979 					} else {
    980 						$enc_style = '';
    981 					}
    982 					$binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
    983 					if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
    984 						$enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
    985 					} else {
    986 						$enc_style = '';
    987 					}
    988 					$binding_xml .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
    989 					$binding_xml .= "\n" . '  </operation>';
    990 					$portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
    991 					if (isset($opParts['parameterOrder'])) {
    992 					    $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
    993 					} 
    994 					$portType_xml .= '>';
    995 					if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
    996 						$portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
    997 					}
    998 					$portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '"/>';
    999 					$portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '"/>';
   1000 					$portType_xml .= "\n" . '  </operation>';
   1001 				} 
   1002 				$portType_xml .= "\n" . '</portType>';
   1003 				$binding_xml .= "\n" . '</binding>';
   1004 			} 
   1005 			$xml .= $portType_xml . $binding_xml;
   1006 		} 
   1007 		// services
   1008 		$xml .= "\n<service name=\"" . $this->serviceName . '">';
   1009 		if (count($this->ports) >= 1) {
   1010 			foreach($this->ports as $pName => $attrs) {
   1011 				$xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
   1012 				$xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
   1013 				$xml .= "\n" . '  </port>';
   1014 			} 
   1015 		} 
   1016 		$xml .= "\n" . '</service>';
   1017 		return $xml . "\n</definitions>";
   1018 	} 
   1019 
   1020 	/**
   1021 	 * determine whether a set of parameters are unwrapped
   1022 	 * when they are expect to be wrapped, Microsoft-style.
   1023 	 *
   1024 	 * @param string $type the type (element name) of the wrapper
   1025 	 * @param array $parameters the parameter values for the SOAP call
   1026 	 * @return boolean whether they parameters are unwrapped (and should be wrapped)
   1027 	 * @access private
   1028 	 */
   1029 	function parametersMatchWrapped($type, &$parameters) {
   1030 		$this->debug("in parametersMatchWrapped type=$type, parameters=");
   1031 		$this->appendDebug($this->varDump($parameters));
   1032 
   1033 		// split type into namespace:unqualified-type
   1034 		if (strpos($type, ':')) {
   1035 			$uqType = substr($type, strrpos($type, ':') + 1);
   1036 			$ns = substr($type, 0, strrpos($type, ':'));
   1037 			$this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
   1038 			if ($this->getNamespaceFromPrefix($ns)) {
   1039 				$ns = $this->getNamespaceFromPrefix($ns);
   1040 				$this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
   1041 			}
   1042 		} else {
   1043 			// TODO: should the type be compared to types in XSD, and the namespace
   1044 			// set to XSD if the type matches?
   1045 			$this->debug("in parametersMatchWrapped: No namespace for type $type");
   1046 			$ns = '';
   1047 			$uqType = $type;
   1048 		}
   1049 
   1050 		// get the type information
   1051 		if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
   1052 			$this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
   1053 			return false;
   1054 		}
   1055 		$this->debug("in parametersMatchWrapped: found typeDef=");
   1056 		$this->appendDebug($this->varDump($typeDef));
   1057 		if (substr($uqType, -1) == '^') {
   1058 			$uqType = substr($uqType, 0, -1);
   1059 		}
   1060 		$phpType = $typeDef['phpType'];
   1061 		$arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
   1062 		$this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
   1063 		
   1064 		// we expect a complexType or element of complexType
   1065 		if ($phpType != 'struct') {
   1066 			$this->debug("in parametersMatchWrapped: not a struct");
   1067 			return false;
   1068 		}
   1069 
   1070 		// see whether the parameter names match the elements
   1071 		if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
   1072 			$elements = 0;
   1073 			$matches = 0;
   1074 			foreach ($typeDef['elements'] as $name => $attrs) {
   1075 				if (isset($parameters[$name])) {
   1076 					$this->debug("in parametersMatchWrapped: have parameter named $name");
   1077 					$matches++;
   1078 				} else {
   1079 					$this->debug("in parametersMatchWrapped: do not have parameter named $name");
   1080 				}
   1081 				$elements++;
   1082 			}
   1083 
   1084 			$this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
   1085 			if ($matches == 0) {
   1086 				return false;
   1087 			}
   1088 			return true;
   1089 		}
   1090 
   1091 		// since there are no elements for the type, if the user passed no
   1092 		// parameters, the parameters match wrapped.
   1093 		$this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
   1094 		return count($parameters) == 0;
   1095 	}
   1096 
   1097 	/**
   1098 	 * serialize PHP values according to a WSDL message definition
   1099 	 * contrary to the method name, this is not limited to RPC
   1100 	 *
   1101 	 * TODO
   1102 	 * - multi-ref serialization
   1103 	 * - validate PHP values against type definitions, return errors if invalid
   1104 	 * 
   1105 	 * @param string $operation operation name
   1106 	 * @param string $direction (input|output)
   1107 	 * @param mixed $parameters parameter value(s)
   1108 	 * @param string $bindingType (soap|soap12)
   1109 	 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
   1110 	 * @access public
   1111 	 */
   1112 	function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
   1113 		$this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
   1114 		$this->appendDebug('parameters=' . $this->varDump($parameters));
   1115 		
   1116 		if ($direction != 'input' && $direction != 'output') {
   1117 			$this->debug('The value of the \$direction argument needs to be either "input" or "output"');
   1118 			$this->setError('The value of the \$direction argument needs to be either "input" or "output"');
   1119 			return false;
   1120 		} 
   1121 		if (!$opData = $this->getOperationData($operation, $bindingType)) {
   1122 			$this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
   1123 			$this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
   1124 			return false;
   1125 		}
   1126 		$this->debug('in serializeRPCParameters: opData:');
   1127 		$this->appendDebug($this->varDump($opData));
   1128 
   1129 		// Get encoding style for output and set to current
   1130 		$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
   1131 		if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
   1132 			$encodingStyle = $opData['output']['encodingStyle'];
   1133 			$enc_style = $encodingStyle;
   1134 		}
   1135 
   1136 		// set input params
   1137 		$xml = '';
   1138 		if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
   1139 			$parts = &$opData[$direction]['parts'];
   1140 			$part_count = sizeof($parts);
   1141 			$style = $opData['style'];
   1142 			$use = $opData[$direction]['use'];
   1143 			$this->debug("have $part_count part(s) to serialize using $style/$use");
   1144 			if (is_array($parameters)) {
   1145 				$parametersArrayType = $this->isArraySimpleOrStruct($parameters);
   1146 				$parameter_count = count($parameters);
   1147 				$this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
   1148 				// check for Microsoft-style wrapped parameters
   1149 				if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
   1150 					$this->debug('check whether the caller has wrapped the parameters');
   1151 					if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
   1152 						// TODO: consider checking here for double-wrapping, when
   1153 						// service function wraps, then NuSOAP wraps again
   1154 						$this->debug("change simple array to associative with 'parameters' element");
   1155 						$parameters['parameters'] = $parameters[0];
   1156 						unset($parameters[0]);
   1157 					}
   1158 					if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
   1159 						$this->debug('check whether caller\'s parameters match the wrapped ones');
   1160 						if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
   1161 							$this->debug('wrap the parameters for the caller');
   1162 							$parameters = array('parameters' => $parameters);
   1163 							$parameter_count = 1;
   1164 						}
   1165 					}
   1166 				}
   1167 				foreach ($parts as $name => $type) {
   1168 					$this->debug("serializing part $name of type $type");
   1169 					// Track encoding style
   1170 					if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
   1171 						$encodingStyle = $opData[$direction]['encodingStyle'];			
   1172 						$enc_style = $encodingStyle;
   1173 					} else {
   1174 						$enc_style = false;
   1175 					}
   1176 					// NOTE: add error handling here
   1177 					// if serializeType returns false, then catch global error and fault
   1178 					if ($parametersArrayType == 'arraySimple') {
   1179 						$p = array_shift($parameters);
   1180 						$this->debug('calling serializeType w/indexed param');
   1181 						$xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
   1182 					} elseif (isset($parameters[$name])) {
   1183 						$this->debug('calling serializeType w/named param');
   1184 						$xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
   1185 					} else {
   1186 						// TODO: only send nillable
   1187 						$this->debug('calling serializeType w/null param');
   1188 						$xml .= $this->serializeType($name, $type, null, $use, $enc_style);
   1189 					}
   1190 				}
   1191 			} else {
   1192 				$this->debug('no parameters passed.');
   1193 			}
   1194 		}
   1195 		$this->debug("serializeRPCParameters returning: $xml");
   1196 		return $xml;
   1197 	} 
   1198 	
   1199 	/**
   1200 	 * serialize a PHP value according to a WSDL message definition
   1201 	 * 
   1202 	 * TODO
   1203 	 * - multi-ref serialization
   1204 	 * - validate PHP values against type definitions, return errors if invalid
   1205 	 * 
   1206 	 * @param string $operation operation name
   1207 	 * @param string $direction (input|output)
   1208 	 * @param mixed $parameters parameter value(s)
   1209 	 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
   1210 	 * @access public
   1211 	 * @deprecated
   1212 	 */
   1213 	function serializeParameters($operation, $direction, $parameters)
   1214 	{
   1215 		$this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion"); 
   1216 		$this->appendDebug('parameters=' . $this->varDump($parameters));
   1217 		
   1218 		if ($direction != 'input' && $direction != 'output') {
   1219 			$this->debug('The value of the \$direction argument needs to be either "input" or "output"');
   1220 			$this->setError('The value of the \$direction argument needs to be either "input" or "output"');
   1221 			return false;
   1222 		} 
   1223 		if (!$opData = $this->getOperationData($operation)) {
   1224 			$this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
   1225 			$this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
   1226 			return false;
   1227 		}
   1228 		$this->debug('opData:');
   1229 		$this->appendDebug($this->varDump($opData));
   1230 		
   1231 		// Get encoding style for output and set to current
   1232 		$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
   1233 		if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
   1234 			$encodingStyle = $opData['output']['encodingStyle'];
   1235 			$enc_style = $encodingStyle;
   1236 		}
   1237 		
   1238 		// set input params
   1239 		$xml = '';
   1240 		if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
   1241 			
   1242 			$use = $opData[$direction]['use'];
   1243 			$this->debug("use=$use");
   1244 			$this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
   1245 			if (is_array($parameters)) {
   1246 				$parametersArrayType = $this->isArraySimpleOrStruct($parameters);
   1247 				$this->debug('have ' . $parametersArrayType . ' parameters');
   1248 				foreach($opData[$direction]['parts'] as $name => $type) {
   1249 					$this->debug('serializing part "'.$name.'" of type "'.$type.'"');
   1250 					// Track encoding style
   1251 					if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
   1252 						$encodingStyle = $opData[$direction]['encodingStyle'];			
   1253 						$enc_style = $encodingStyle;
   1254 					} else {
   1255 						$enc_style = false;
   1256 					}
   1257 					// NOTE: add error handling here
   1258 					// if serializeType returns false, then catch global error and fault
   1259 					if ($parametersArrayType == 'arraySimple') {
   1260 						$p = array_shift($parameters);
   1261 						$this->debug('calling serializeType w/indexed param');
   1262 						$xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
   1263 					} elseif (isset($parameters[$name])) {
   1264 						$this->debug('calling serializeType w/named param');
   1265 						$xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
   1266 					} else {
   1267 						// TODO: only send nillable
   1268 						$this->debug('calling serializeType w/null param');
   1269 						$xml .= $this->serializeType($name, $type, null, $use, $enc_style);
   1270 					}
   1271 				}
   1272 			} else {
   1273 				$this->debug('no parameters passed.');
   1274 			}
   1275 		}
   1276 		$this->debug("serializeParameters returning: $xml");
   1277 		return $xml;
   1278 	} 
   1279 	
   1280 	/**
   1281 	 * serializes a PHP value according a given type definition
   1282 	 * 
   1283 	 * @param string $name name of value (part or element)
   1284 	 * @param string $type XML schema type of value (type or element)
   1285 	 * @param mixed $value a native PHP value (parameter value)
   1286 	 * @param string $use use for part (encoded|literal)
   1287 	 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
   1288 	 * @param boolean $unqualified a kludge for what should be XML namespace form handling
   1289 	 * @return string value serialized as an XML string
   1290 	 * @access private
   1291 	 */
   1292 	function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
   1293 	{
   1294 		$this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
   1295 		$this->appendDebug("value=" . $this->varDump($value));
   1296 		if($use == 'encoded' && $encodingStyle) {
   1297 			$encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
   1298 		}
   1299 
   1300 		// if a soapval has been supplied, let its type override the WSDL
   1301     	if (is_object($value) && get_class($value) == 'soapval') {
   1302     		if ($value->type_ns) {
   1303     			$type = $value->type_ns . ':' . $value->type;
   1304 		    	$forceType = true;
   1305 		    	$this->debug("in serializeType: soapval overrides type to $type");
   1306     		} elseif ($value->type) {
   1307 	    		$type = $value->type;
   1308 		    	$forceType = true;
   1309 		    	$this->debug("in serializeType: soapval overrides type to $type");
   1310 	    	} else {
   1311 	    		$forceType = false;
   1312 		    	$this->debug("in serializeType: soapval does not override type");
   1313 	    	}
   1314 	    	$attrs = $value->attributes;
   1315 	    	$value = $value->value;
   1316 	    	$this->debug("in serializeType: soapval overrides value to $value");
   1317 	    	if ($attrs) {
   1318 	    		if (!is_array($value)) {
   1319 	    			$value['!'] = $value;
   1320 	    		}
   1321 	    		foreach ($attrs as $n => $v) {
   1322 	    			$value['!' . $n] = $v;
   1323 	    		}
   1324 		    	$this->debug("in serializeType: soapval provides attributes");
   1325 		    }
   1326         } else {
   1327         	$forceType = false;
   1328         }
   1329 
   1330 		$xml = '';
   1331 		if (strpos($type, ':')) {
   1332 			$uqType = substr($type, strrpos($type, ':') + 1);
   1333 			$ns = substr($type, 0, strrpos($type, ':'));
   1334 			$this->debug("in serializeType: got a prefixed type: $uqType, $ns");
   1335 			if ($this->getNamespaceFromPrefix($ns)) {
   1336 				$ns = $this->getNamespaceFromPrefix($ns);
   1337 				$this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
   1338 			}
   1339 
   1340 			if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
   1341 				$this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
   1342 				if ($unqualified && $use == 'literal') {
   1343 					$elementNS = " xmlns=\"\"";
   1344 				} else {
   1345 					$elementNS = '';
   1346 				}
   1347 				if (is_null($value)) {
   1348 					if ($use == 'literal') {
   1349 						// TODO: depends on minOccurs
   1350 						$xml = "<$name$elementNS/>";
   1351 					} else {
   1352 						// TODO: depends on nillable, which should be checked before calling this method
   1353 						$xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
   1354 					}
   1355 					$this->debug("in serializeType: returning: $xml");
   1356 					return $xml;
   1357 				}
   1358 				if ($uqType == 'Array') {
   1359 					// JBoss/Axis does this sometimes
   1360 					return $this->serialize_val($value, $name, false, false, false, false, $use);
   1361 				}
   1362 		    	if ($uqType == 'boolean') {
   1363 		    		if ((is_string($value) && $value == 'false') || (! $value)) {
   1364 						$value = 'false';
   1365 					} else {
   1366 						$value = 'true';
   1367 					}
   1368 				} 
   1369 				if ($uqType == 'string' && gettype($value) == 'string') {
   1370 					$value = $this->expandEntities($value);
   1371 				}
   1372 				if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
   1373 					$value = sprintf("%.0lf", $value);
   1374 				}
   1375 				// it's a scalar
   1376 				// TODO: what about null/nil values?
   1377 				// check type isn't a custom type extending xmlschema namespace
   1378 				if (!$this->getTypeDef($uqType, $ns)) {
   1379 					if ($use == 'literal') {
   1380 						if ($forceType) {
   1381 							$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
   1382 						} else {
   1383 							$xml = "<$name$elementNS>$value</$name>";
   1384 						}
   1385 					} else {
   1386 						$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
   1387 					}
   1388 					$this->debug("in serializeType: returning: $xml");
   1389 					return $xml;
   1390 				}
   1391 				$this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
   1392 			} else if ($ns == 'http://xml.apache.org/xml-soap') {
   1393 				$this->debug('in serializeType: appears to be Apache SOAP type');
   1394 				if ($uqType == 'Map') {
   1395 					$tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
   1396 					if (! $tt_prefix) {
   1397 						$this->debug('in serializeType: Add namespace for Apache SOAP type');
   1398 						$tt_prefix = 'ns' . rand(1000, 9999);
   1399 						$this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
   1400 						// force this to be added to usedNamespaces
   1401 						$tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
   1402 					}
   1403 					$contents = '';
   1404 					foreach($value as $k => $v) {
   1405 						$this->debug("serializing map element: key $k, value $v");
   1406 						$contents .= '<item>';
   1407 						$contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
   1408 						$contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
   1409 						$contents .= '</item>';
   1410 					}
   1411 					if ($use == 'literal') {
   1412 						if ($forceType) {
   1413 							$xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
   1414 						} else {
   1415 							$xml = "<$name>$contents</$name>";
   1416 						}
   1417 					} else {
   1418 						$xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
   1419 					}
   1420 					$this->debug("in serializeType: returning: $xml");
   1421 					return $xml;
   1422 				}
   1423 				$this->debug('in serializeType: Apache SOAP type, but only support Map');
   1424 			}
   1425 		} else {
   1426 			// TODO: should the type be compared to types in XSD, and the namespace
   1427 			// set to XSD if the type matches?
   1428 			$this->debug("in serializeType: No namespace for type $type");
   1429 			$ns = '';
   1430 			$uqType = $type;
   1431 		}
   1432 		if(!$typeDef = $this->getTypeDef($uqType, $ns)){
   1433 			$this->setError("$type ($uqType) is not a supported type.");
   1434 			$this->debug("in serializeType: $type ($uqType) is not a supported type.");
   1435 			return false;
   1436 		} else {
   1437 			$this->debug("in serializeType: found typeDef");
   1438 			$this->appendDebug('typeDef=' . $this->varDump($typeDef));
   1439 			if (substr($uqType, -1) == '^') {
   1440 				$uqType = substr($uqType, 0, -1);
   1441 			}
   1442 		}
   1443 		if (!isset($typeDef['phpType'])) {
   1444 			$this->setError("$type ($uqType) has no phpType.");
   1445 			$this->debug("in serializeType: $type ($uqType) has no phpType.");
   1446 			return false;
   1447 		}
   1448 		$phpType = $typeDef['phpType'];
   1449 		$this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); 
   1450 		// if php type == struct, map value to the <all> element names
   1451 		if ($phpType == 'struct') {
   1452 			if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
   1453 				$elementName = $uqType;
   1454 				if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
   1455 					$elementNS = " xmlns=\"$ns\"";
   1456 				} else {
   1457 					$elementNS = " xmlns=\"\"";
   1458 				}
   1459 			} else {
   1460 				$elementName = $name;
   1461 				if ($unqualified) {
   1462 					$elementNS = " xmlns=\"\"";
   1463 				} else {
   1464 					$elementNS = '';
   1465 				}
   1466 			}
   1467 			if (is_null($value)) {
   1468 				if ($use == 'literal') {
   1469 					// TODO: depends on minOccurs and nillable
   1470 					$xml = "<$elementName$elementNS/>";
   1471 				} else {
   1472 					$xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
   1473 				}
   1474 				$this->debug("in serializeType: returning: $xml");
   1475 				return $xml;
   1476 			}
   1477 			if (is_object($value)) {
   1478 				$value = get_object_vars($value);
   1479 			}
   1480 			if (is_array($value)) {
   1481 				$elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
   1482 				if ($use == 'literal') {
   1483 					if ($forceType) {
   1484 						$xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
   1485 					} else {
   1486 						$xml = "<$elementName$elementNS$elementAttrs>";
   1487 					}
   1488 				} else {
   1489 					$xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
   1490 				}
   1491 
   1492 				if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
   1493 					if (isset($value['!'])) {
   1494 						$xml .= $value['!'];
   1495 						$this->debug("in serializeType: serialized simpleContent for type $type");
   1496 					} else {
   1497 						$this->debug("in serializeType: no simpleContent to serialize for type $type");
   1498 					}
   1499 				} else {
   1500 					// complexContent
   1501 					$xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
   1502 				}
   1503 				$xml .= "</$elementName>";
   1504 			} else {
   1505 				$this->debug("in serializeType: phpType is struct, but value is not an array");
   1506 				$this->setError("phpType is struct, but value is not an array: see debug output for details");
   1507 				$xml = '';
   1508 			}
   1509 		} elseif ($phpType == 'array') {
   1510 			if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
   1511 				$elementNS = " xmlns=\"$ns\"";
   1512 			} else {
   1513 				if ($unqualified) {
   1514 					$elementNS = " xmlns=\"\"";
   1515 				} else {
   1516 					$elementNS = '';
   1517 				}
   1518 			}
   1519 			if (is_null($value)) {
   1520 				if ($use == 'literal') {
   1521 					// TODO: depends on minOccurs
   1522 					$xml = "<$name$elementNS/>";
   1523 				} else {
   1524 					$xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
   1525 						$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
   1526 						":Array\" " .
   1527 						$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
   1528 						':arrayType="' .
   1529 						$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
   1530 						':' .
   1531 						$this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
   1532 				}
   1533 				$this->debug("in serializeType: returning: $xml");
   1534 				return $xml;
   1535 			}
   1536 			if (isset($typeDef['multidimensional'])) {
   1537 				$nv = array();
   1538 				foreach($value as $v) {
   1539 					$cols = ',' . sizeof($v);
   1540 					$nv = array_merge($nv, $v);
   1541 				} 
   1542 				$value = $nv;
   1543 			} else {
   1544 				$cols = '';
   1545 			} 
   1546 			if (is_array($value) && sizeof($value) >= 1) {
   1547 				$rows = sizeof($value);
   1548 				$contents = '';
   1549 				foreach($value as $k => $v) {
   1550 					$this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
   1551 					//if (strpos($typeDef['arrayType'], ':') ) {
   1552 					if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
   1553 					    $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
   1554 					} else {
   1555 					    $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
   1556 					} 
   1557 				}
   1558 			} else {
   1559 				$rows = 0;
   1560 				$contents = null;
   1561 			}
   1562 			// TODO: for now, an empty value will be serialized as a zero element
   1563 			// array.  Revisit this when coding the handling of null/nil values.
   1564 			if ($use == 'literal') {
   1565 				$xml = "<$name$elementNS>"
   1566 					.$contents
   1567 					."</$name>";
   1568 			} else {
   1569 				$xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
   1570 					$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
   1571 					.':arrayType="'
   1572 					.$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
   1573 					.":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
   1574 					.$contents
   1575 					."</$name>";
   1576 			}
   1577 		} elseif ($phpType == 'scalar') {
   1578 			if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
   1579 				$elementNS = " xmlns=\"$ns\"";
   1580 			} else {
   1581 				if ($unqualified) {
   1582 					$elementNS = " xmlns=\"\"";
   1583 				} else {
   1584 					$elementNS = '';
   1585 				}
   1586 			}
   1587 			if ($use == 'literal') {
   1588 				if ($forceType) {
   1589 					$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
   1590 				} else {
   1591 					$xml = "<$name$elementNS>$value</$name>";
   1592 				}
   1593 			} else {
   1594 				$xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
   1595 			}
   1596 		}
   1597 		$this->debug("in serializeType: returning: $xml");
   1598 		return $xml;
   1599 	}
   1600 	
   1601 	/**
   1602 	 * serializes the attributes for a complexType
   1603 	 *
   1604 	 * @param array $typeDef our internal representation of an XML schema type (or element)
   1605 	 * @param mixed $value a native PHP value (parameter value)
   1606 	 * @param string $ns the namespace of the type
   1607 	 * @param string $uqType the local part of the type
   1608 	 * @return string value serialized as an XML string
   1609 	 * @access private
   1610 	 */
   1611 	function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
   1612 		$this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
   1613 		$xml = '';
   1614 		if (isset($typeDef['extensionBase'])) {
   1615 			$nsx = $this->getPrefix($typeDef['extensionBase']);
   1616 			$uqTypex = $this->getLocalPart($typeDef['extensionBase']);
   1617 			if ($this->getNamespaceFromPrefix($nsx)) {
   1618 				$nsx = $this->getNamespaceFromPrefix($nsx);
   1619 			}
   1620 			if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
   1621 				$this->debug("serialize attributes for extension base $nsx:$uqTypex");
   1622 				$xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
   1623 			} else {
   1624 				$this->debug("extension base $nsx:$uqTypex is not a supported type");
   1625 			}
   1626 		}
   1627 		if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
   1628 			$this->debug("serialize attributes for XML Schema type $ns:$uqType");
   1629 			if (is_array($value)) {
   1630 				$xvalue = $value;
   1631 			} elseif (is_object($value)) {
   1632 				$xvalue = get_object_vars($value);
   1633 			} else {
   1634 				$this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
   1635 				$xvalue = array();
   1636 			}
   1637 			foreach ($typeDef['attrs'] as $aName => $attrs) {
   1638 				if (isset($xvalue['!' . $aName])) {
   1639 					$xname = '!' . $aName;
   1640 					$this->debug("value provided for attribute $aName with key $xname");
   1641 				} elseif (isset($xvalue[$aName])) {
   1642 					$xname = $aName;
   1643 					$this->debug("value provided for attribute $aName with key $xname");
   1644 				} elseif (isset($attrs['default'])) {
   1645 					$xname = '!' . $aName;
   1646 					$xvalue[$xname] = $attrs['default'];
   1647 					$this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
   1648 				} else {
   1649 					$xname = '';
   1650 					$this->debug("no value provided for attribute $aName");
   1651 				}
   1652 				if ($xname) {
   1653 					$xml .=  " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
   1654 				}
   1655 			} 
   1656 		} else {
   1657 			$this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
   1658 		}
   1659 		return $xml;
   1660 	}
   1661 
   1662 	/**
   1663 	 * serializes the elements for a complexType
   1664 	 *
   1665 	 * @param array $typeDef our internal representation of an XML schema type (or element)
   1666 	 * @param mixed $value a native PHP value (parameter value)
   1667 	 * @param string $ns the namespace of the type
   1668 	 * @param string $uqType the local part of the type
   1669 	 * @param string $use use for part (encoded|literal)
   1670 	 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
   1671 	 * @return string value serialized as an XML string
   1672 	 * @access private
   1673 	 */
   1674 	function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
   1675 		$this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
   1676 		$xml = '';
   1677 		if (isset($typeDef['extensionBase'])) {
   1678 			$nsx = $this->getPrefix($typeDef['extensionBase']);
   1679 			$uqTypex = $this->getLocalPart($typeDef['extensionBase']);
   1680 			if ($this->getNamespaceFromPrefix($nsx)) {
   1681 				$nsx = $this->getNamespaceFromPrefix($nsx);
   1682 			}
   1683 			if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
   1684 				$this->debug("serialize elements for extension base $nsx:$uqTypex");
   1685 				$xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
   1686 			} else {
   1687 				$this->debug("extension base $nsx:$uqTypex is not a supported type");
   1688 			}
   1689 		}
   1690 		if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
   1691 			$this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
   1692 			if (is_array($value)) {
   1693 				$xvalue = $value;
   1694 			} elseif (is_object($value)) {
   1695 				$xvalue = get_object_vars($value);
   1696 			} else {
   1697 				$this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
   1698 				$xvalue = array();
   1699 			}
   1700 			// toggle whether all elements are present - ideally should validate against schema
   1701 			if (count($typeDef['elements']) != count($xvalue)){
   1702 				$optionals = true;
   1703 			}
   1704 			foreach ($typeDef['elements'] as $eName => $attrs) {
   1705 				if (!isset($xvalue[$eName])) {
   1706 					if (isset($attrs['default'])) {
   1707 						$xvalue[$eName] = $attrs['default'];
   1708 						$this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
   1709 					}
   1710 				}
   1711 				// if user took advantage of a minOccurs=0, then only serialize named parameters
   1712 				if (isset($optionals)
   1713 				    && (!isset($xvalue[$eName])) 
   1714 					&& ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
   1715 					){
   1716 					if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
   1717 						$this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
   1718 					}
   1719 					// do nothing
   1720 					$this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
   1721 				} else {
   1722 					// get value
   1723 					if (isset($xvalue[$eName])) {
   1724 					    $v = $xvalue[$eName];
   1725 					} else {
   1726 					    $v = null;
   1727 					}
   1728 					if (isset($attrs['form'])) {
   1729 						$unqualified = ($attrs['form'] == 'unqualified');
   1730 					} else {
   1731 						$unqualified = false;
   1732 					}
   1733 					if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
   1734 						$vv = $v;
   1735 						foreach ($vv as $k => $v) {
   1736 							if (isset($attrs['type']) || isset($attrs['ref'])) {
   1737 								// serialize schema-defined type
   1738 							    $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
   1739 							} else {
   1740 								// serialize generic type (can this ever really happen?)
   1741 							    $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
   1742 							    $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
   1743 							}
   1744 						}
   1745 					} else {
   1746 						if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
   1747 							// do nothing
   1748 						} elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
   1749 							// TODO: serialize a nil correctly, but for now serialize schema-defined type
   1750 						    $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
   1751 						} elseif (isset($attrs['type']) || isset($attrs['ref'])) {
   1752 							// serialize schema-defined type
   1753 						    $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
   1754 						} else {
   1755 							// serialize generic type (can this ever really happen?)
   1756 						    $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
   1757 						    $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
   1758 						}
   1759 					}
   1760 				}
   1761 			} 
   1762 		} else {
   1763 			$this->debug("no elements to serialize for XML Schema type $ns:$uqType");
   1764 		}
   1765 		return $xml;
   1766 	}
   1767 
   1768 	/**
   1769 	* adds an XML Schema complex type to the WSDL types
   1770 	*
   1771 	* @param string	$name
   1772 	* @param string $typeClass (complexType|simpleType|attribute)
   1773 	* @param string $phpType currently supported are array and struct (php assoc array)
   1774 	* @param string $compositor (all|sequence|choice)
   1775 	* @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
   1776 	* @param array $elements e.g. array ( name => array(name=>'',type=>'') )
   1777 	* @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
   1778 	* @param string $arrayType as namespace:name (xsd:string)
   1779 	* @see nusoap_xmlschema
   1780 	* @access public
   1781 	*/
   1782 	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
   1783 		if (count($elements) > 0) {
   1784 			$eElements = array();
   1785 	    	foreach($elements as $n => $e){
   1786 	            // expand each element
   1787 	            $ee = array();
   1788 	            foreach ($e as $k => $v) {
   1789 		            $k = strpos($k,':') ? $this->expandQname($k) : $k;
   1790 		            $v = strpos($v,':') ? $this->expandQname($v) : $v;
   1791 		            $ee[$k] = $v;
   1792 		    	}
   1793 	    		$eElements[$n] = $ee;
   1794 	    	}
   1795 	    	$elements = $eElements;
   1796 		}
   1797 		
   1798 		if (count($attrs) > 0) {
   1799 	    	foreach($attrs as $n => $a){
   1800 	            // expand each attribute
   1801 	            foreach ($a as $k => $v) {
   1802 		            $k = strpos($k,':') ? $this->expandQname($k) : $k;
   1803 		            $v = strpos($v,':') ? $this->expandQname($v) : $v;
   1804 		            $aa[$k] = $v;
   1805 		    	}
   1806 	    		$eAttrs[$n] = $aa;
   1807 	    	}
   1808 	    	$attrs = $eAttrs;
   1809 		}
   1810 
   1811 		$restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
   1812 		$arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
   1813 
   1814 		$typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
   1815 		$this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
   1816 	}
   1817 
   1818 	/**
   1819 	* adds an XML Schema simple type to the WSDL types
   1820 	*
   1821 	* @param string $name
   1822 	* @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
   1823 	* @param string $typeClass (should always be simpleType)
   1824 	* @param string $phpType (should always be scalar)
   1825 	* @param array $enumeration array of values
   1826 	* @see nusoap_xmlschema
   1827 	* @access public
   1828 	*/
   1829 	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
   1830 		$restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
   1831 
   1832 		$typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
   1833 		$this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
   1834 	}
   1835 
   1836 	/**
   1837 	* adds an element to the WSDL types
   1838 	*
   1839 	* @param array $attrs attributes that must include name and type
   1840 	* @see nusoap_xmlschema
   1841 	* @access public
   1842 	*/
   1843 	function addElement($attrs) {
   1844 		$typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
   1845 		$this->schemas[$typens][0]->addElement($attrs);
   1846 	}
   1847 
   1848 	/**
   1849 	* register an operation with the server
   1850 	* 
   1851 	* @param string $name operation (method) name
   1852 	* @param array $in assoc array of input values: key = param name, value = param type
   1853 	* @param array $out assoc array of output values: key = param name, value = param type
   1854 	* @param string $namespace optional The namespace for the operation
   1855 	* @param string $soapaction optional The soapaction for the operation
   1856 	* @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
   1857 	* @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
   1858 	* @param string $documentation optional The description to include in the WSDL
   1859 	* @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
   1860 	* @access public 
   1861 	*/
   1862 	function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
   1863 		if ($use == 'encoded' && $encodingStyle == '') {
   1864 			$encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
   1865 		}
   1866 
   1867 		if ($style == 'document') {
   1868 			$elements = array();
   1869 			foreach ($in as $n => $t) {
   1870 				$elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
   1871 			}
   1872 			$this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
   1873 			$this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
   1874 			$in = array('parameters' => 'tns:' . $name . '^');
   1875 
   1876 			$elements = array();
   1877 			foreach ($out as $n => $t) {
   1878 				$elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
   1879 			}
   1880 			$this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
   1881 			$this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
   1882 			$out = array('parameters' => 'tns:' . $name . 'Response' . '^');
   1883 		}
   1884 
   1885 		// get binding
   1886 		$this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
   1887 		array(
   1888 		'name' => $name,
   1889 		'binding' => $this->serviceName . 'Binding',
   1890 		'endpoint' => $this->endpoint,
   1891 		'soapAction' => $soapaction,
   1892 		'style' => $style,
   1893 		'input' => array(
   1894 			'use' => $use,
   1895 			'namespace' => $namespace,
   1896 			'encodingStyle' => $encodingStyle,
   1897 			'message' => $name . 'Request',
   1898 			'parts' => $in),
   1899 		'output' => array(
   1900 			'use' => $use,
   1901 			'namespace' => $namespace,
   1902 			'encodingStyle' => $encodingStyle,
   1903 			'message' => $name . 'Response',
   1904 			'parts' => $out),
   1905 		'namespace' => $namespace,
   1906 		'transport' => 'http://schemas.xmlsoap.org/soap/http',
   1907 		'documentation' => $documentation); 
   1908 		// add portTypes
   1909 		// add messages
   1910 		if($in)
   1911 		{
   1912 			foreach($in as $pName => $pType)
   1913 			{
   1914 				if(strpos($pType,':')) {
   1915 					$pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
   1916 				}
   1917 				$this->messages[$name.'Request'][$pName] = $pType;
   1918 			}
   1919 		} else {
   1920             $this->messages[$name.'Request']= '0';
   1921         }
   1922 		if($out)
   1923 		{
   1924 			foreach($out as $pName => $pType)
   1925 			{
   1926 				if(strpos($pType,':')) {
   1927 					$pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
   1928 				}
   1929 				$this->messages[$name.'Response'][$pName] = $pType;
   1930 			}
   1931 		} else {
   1932             $this->messages[$name.'Response']= '0';
   1933         }
   1934 		return true;
   1935 	} 
   1936 }
   1937 
   1938 ?>