nusoap_parser

Nusoap_parser class parses SOAP XML messages into native PHP values.

Defined (2)

The class is defined in the following location(s).

/lib/class.soap_parser.php  
  1. class nusoap_parser extends nusoap_base { 
  2.  
  3. var $xml = ''; 
  4. var $xml_encoding = ''; 
  5. var $method = ''; 
  6. var $root_struct = ''; 
  7. var $root_struct_name = ''; 
  8. var $root_struct_namespace = ''; 
  9. var $root_header = ''; 
  10. var $document = ''; // incoming SOAP body (text) 
  11. // determines where in the message we are (envelope, header, body, method) 
  12. var $status = ''; 
  13. var $position = 0; 
  14. var $depth = 0; 
  15. var $default_namespace = ''; 
  16. var $namespaces = array(); 
  17. var $message = array(); 
  18. var $parent = ''; 
  19. var $fault = false; 
  20. var $fault_code = ''; 
  21. var $fault_str = ''; 
  22. var $fault_detail = ''; 
  23. var $depth_array = array(); 
  24. var $debug_flag = true; 
  25. var $soapresponse = NULL; // parsed SOAP Body 
  26. var $soapheader = NULL; // parsed SOAP Header 
  27. var $responseHeaders = ''; // incoming SOAP headers (text) 
  28. var $body_position = 0; 
  29. // for multiref parsing: 
  30. // array of id => pos 
  31. var $ids = array(); 
  32. // array of id => hrefs => pos 
  33. var $multirefs = array(); 
  34. // toggle for auto-decoding element content 
  35. var $decode_utf8 = true; 
  36.  
  37. /** 
  38. * constructor that actually does the parsing 
  39. * @param string $xml SOAP message 
  40. * @param string $encoding character encoding scheme of message 
  41. * @param string $method method for which XML is parsed (unused?) 
  42. * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1 
  43. * @access public 
  44. */ 
  45. function nusoap_parser($xml, $encoding='UTF-8', $method='', $decode_utf8=true) { 
  46. parent::nusoap_base(); 
  47. $this->xml = $xml; 
  48. $this->xml_encoding = $encoding; 
  49. $this->method = $method; 
  50. $this->decode_utf8 = $decode_utf8; 
  51.  
  52. // Check whether content has been read. 
  53. if(!empty($xml)) { 
  54. // Check XML encoding 
  55. $pos_xml = strpos($xml, '<?xml'); 
  56. if ($pos_xml !== FALSE) { 
  57. $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1); 
  58. if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) { 
  59. $xml_encoding = $res[1]; 
  60. if (strtoupper($xml_encoding) != $encoding) { 
  61. $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'"; 
  62. $this->debug($err); 
  63. if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') { 
  64. $this->setError($err); 
  65. return; 
  66. // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed 
  67. } else { 
  68. $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration'); 
  69. } else { 
  70. $this->debug('No encoding specified in XML declaration'); 
  71. } else { 
  72. $this->debug('No XML declaration'); 
  73. $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding); 
  74. // Create an XML parser - why not xml_parser_create_ns? 
  75. $this->parser = xml_parser_create($this->xml_encoding); 
  76. // Set the options for parsing the XML data. 
  77. //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); 
  78. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
  79. xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding); 
  80. // Set the object for the parser. 
  81. xml_set_object($this->parser, $this); 
  82. // Set the element handlers for the parser. 
  83. xml_set_element_handler($this->parser, 'start_element', 'end_element'); 
  84. xml_set_character_data_handler($this->parser, 'character_data'); 
  85.  
  86. // Parse the XML file. 
  87. if(!xml_parse($this->parser, $xml, true)) { 
  88. // Display an error message. 
  89. $err = sprintf('XML error parsing SOAP payload on line %d: %s',  
  90. xml_get_current_line_number($this->parser),  
  91. xml_error_string(xml_get_error_code($this->parser))); 
  92. $this->debug($err); 
  93. $this->debug("XML payload:\n" . $xml); 
  94. $this->setError($err); 
  95. } else { 
  96. $this->debug('in nusoap_parser ctor, message:'); 
  97. $this->appendDebug($this->varDump($this->message)); 
  98. $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name); 
  99. // get final value 
  100. $this->soapresponse = $this->message[$this->root_struct]['result']; 
  101. // get header value 
  102. if($this->root_header != '' && isset($this->message[$this->root_header]['result'])) { 
  103. $this->soapheader = $this->message[$this->root_header]['result']; 
  104. // resolve hrefs/ids 
  105. if(sizeof($this->multirefs) > 0) { 
  106. foreach($this->multirefs as $id => $hrefs) { 
  107. $this->debug('resolving multirefs for id: '.$id); 
  108. $idVal = $this->buildVal($this->ids[$id]); 
  109. if (is_array($idVal) && isset($idVal['!id'])) { 
  110. unset($idVal['!id']); 
  111. foreach($hrefs as $refPos => $ref) { 
  112. $this->debug('resolving href at pos '.$refPos); 
  113. $this->multirefs[$id][$refPos] = $idVal; 
  114. xml_parser_free($this->parser); 
  115. } else { 
  116. $this->debug('xml was empty, didn\'t parse!'); 
  117. $this->setError('xml was empty, didn\'t parse!'); 
  118.  
  119. /** 
  120. * start-element handler 
  121. * @param resource $parser XML parser object 
  122. * @param string $name element name 
  123. * @param array $attrs associative array of attributes 
  124. * @access private 
  125. */ 
  126. function start_element($parser, $name, $attrs) { 
  127. // position in a total number of elements, starting from 0 
  128. // update class level pos 
  129. $pos = $this->position++; 
  130. // and set mine 
  131. $this->message[$pos] = array('pos' => $pos, 'children'=>'', 'cdata'=>''); 
  132. // depth = how many levels removed from root? 
  133. // set mine as current global depth and increment global depth value 
  134. $this->message[$pos]['depth'] = $this->depth++; 
  135.  
  136. // else add self as child to whoever the current parent is 
  137. if($pos != 0) { 
  138. $this->message[$this->parent]['children'] .= '|'.$pos; 
  139. // set my parent 
  140. $this->message[$pos]['parent'] = $this->parent; 
  141. // set self as current parent 
  142. $this->parent = $pos; 
  143. // set self as current value for this depth 
  144. $this->depth_array[$this->depth] = $pos; 
  145. // get element prefix 
  146. if(strpos($name, ':')) { 
  147. // get ns prefix 
  148. $prefix = substr($name, 0, strpos($name, ':')); 
  149. // get unqualified name 
  150. $name = substr(strstr($name, ':'), 1); 
  151. // set status 
  152. if ($name == 'Envelope' && $this->status == '') { 
  153. $this->status = 'envelope'; 
  154. } elseif ($name == 'Header' && $this->status == 'envelope') { 
  155. $this->root_header = $pos; 
  156. $this->status = 'header'; 
  157. } elseif ($name == 'Body' && $this->status == 'envelope') { 
  158. $this->status = 'body'; 
  159. $this->body_position = $pos; 
  160. // set method 
  161. } elseif($this->status == 'body' && $pos == ($this->body_position+1)) { 
  162. $this->status = 'method'; 
  163. $this->root_struct_name = $name; 
  164. $this->root_struct = $pos; 
  165. $this->message[$pos]['type'] = 'struct'; 
  166. $this->debug("found root struct $this->root_struct_name, pos $this->root_struct"); 
  167. // set my status 
  168. $this->message[$pos]['status'] = $this->status; 
  169. // set name 
  170. $this->message[$pos]['name'] = htmlspecialchars($name); 
  171. // set attrs 
  172. $this->message[$pos]['attrs'] = $attrs; 
  173.  
  174. // loop through atts, logging ns and type declarations 
  175. $attstr = ''; 
  176. foreach($attrs as $key => $value) { 
  177. $key_prefix = $this->getPrefix($key); 
  178. $key_localpart = $this->getLocalPart($key); 
  179. // if ns declarations, add to class level array of valid namespaces 
  180. if($key_prefix == 'xmlns') { 
  181. if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) { 
  182. $this->XMLSchemaVersion = $value; 
  183. $this->namespaces['xsd'] = $this->XMLSchemaVersion; 
  184. $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance'; 
  185. $this->namespaces[$key_localpart] = $value; 
  186. // set method namespace 
  187. if($name == $this->root_struct_name) { 
  188. $this->methodNamespace = $value; 
  189. // if it's a type declaration, set type 
  190. } elseif($key_localpart == 'type') { 
  191. if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') { 
  192. // do nothing: already processed arrayType 
  193. } else { 
  194. $value_prefix = $this->getPrefix($value); 
  195. $value_localpart = $this->getLocalPart($value); 
  196. $this->message[$pos]['type'] = $value_localpart; 
  197. $this->message[$pos]['typePrefix'] = $value_prefix; 
  198. if(isset($this->namespaces[$value_prefix])) { 
  199. $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix]; 
  200. } else if(isset($attrs['xmlns:'.$value_prefix])) { 
  201. $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix]; 
  202. // should do something here with the namespace of specified type? 
  203. } elseif($key_localpart == 'arrayType') { 
  204. $this->message[$pos]['type'] = 'array'; 
  205. /** do arrayType ereg here 
  206. [1] arrayTypeValue ::= atype asize 
  207. [2] atype ::= QName rank* 
  208. [3] rank ::= '[' (', ')* ']' 
  209. [4] asize ::= '[' length~ ']' 
  210. [5] length ::= nextDimension* Digit+ 
  211. [6] nextDimension ::= Digit+ ', ' 
  212. */ 
  213. $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+), ?([0-9]*)\]/'; 
  214. if(preg_match($expr, $value, $regs)) { 
  215. $this->message[$pos]['typePrefix'] = $regs[1]; 
  216. $this->message[$pos]['arrayTypePrefix'] = $regs[1]; 
  217. if (isset($this->namespaces[$regs[1]])) { 
  218. $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]]; 
  219. } else if (isset($attrs['xmlns:'.$regs[1]])) { 
  220. $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]]; 
  221. $this->message[$pos]['arrayType'] = $regs[2]; 
  222. $this->message[$pos]['arraySize'] = $regs[3]; 
  223. $this->message[$pos]['arrayCols'] = $regs[4]; 
  224. // specifies nil value (or not) 
  225. } elseif ($key_localpart == 'nil') { 
  226. $this->message[$pos]['nil'] = ($value == 'true' || $value == '1'); 
  227. // some other attribute 
  228. } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') { 
  229. $this->message[$pos]['xattrs']['!' . $key] = $value; 
  230.  
  231. if ($key == 'xmlns') { 
  232. $this->default_namespace = $value; 
  233. // log id 
  234. if($key == 'id') { 
  235. $this->ids[$value] = $pos; 
  236. // root 
  237. if($key_localpart == 'root' && $value == 1) { 
  238. $this->status = 'method'; 
  239. $this->root_struct_name = $name; 
  240. $this->root_struct = $pos; 
  241. $this->debug("found root struct $this->root_struct_name, pos $pos"); 
  242. // for doclit 
  243. $attstr .= " $key=\"$value\""; 
  244. // get namespace - must be done after namespace atts are processed 
  245. if(isset($prefix)) { 
  246. $this->message[$pos]['namespace'] = $this->namespaces[$prefix]; 
  247. $this->default_namespace = $this->namespaces[$prefix]; 
  248. } else { 
  249. $this->message[$pos]['namespace'] = $this->default_namespace; 
  250. if($this->status == 'header') { 
  251. if ($this->root_header != $pos) { 
  252. $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; 
  253. } elseif($this->root_struct_name != '') { 
  254. $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; 
  255.  
  256. /** 
  257. * end-element handler 
  258. * @param resource $parser XML parser object 
  259. * @param string $name element name 
  260. * @access private 
  261. */ 
  262. function end_element($parser, $name) { 
  263. // position of current element is equal to the last value left in depth_array for my depth 
  264. $pos = $this->depth_array[$this->depth--]; 
  265.  
  266. // get element prefix 
  267. if(strpos($name, ':')) { 
  268. // get ns prefix 
  269. $prefix = substr($name, 0, strpos($name, ':')); 
  270. // get unqualified name 
  271. $name = substr(strstr($name, ':'), 1); 
  272.  
  273. // build to native type 
  274. if(isset($this->body_position) && $pos > $this->body_position) { 
  275. // deal w/ multirefs 
  276. if(isset($this->message[$pos]['attrs']['href'])) { 
  277. // get id 
  278. $id = substr($this->message[$pos]['attrs']['href'], 1); 
  279. // add placeholder to href array 
  280. $this->multirefs[$id][$pos] = 'placeholder'; 
  281. // add set a reference to it as the result value 
  282. $this->message[$pos]['result'] =& $this->multirefs[$id][$pos]; 
  283. // build complexType values 
  284. } elseif($this->message[$pos]['children'] != '') { 
  285. // if result has already been generated (struct/array) 
  286. if(!isset($this->message[$pos]['result'])) { 
  287. $this->message[$pos]['result'] = $this->buildVal($pos); 
  288. // build complexType values of attributes and possibly simpleContent 
  289. } elseif (isset($this->message[$pos]['xattrs'])) { 
  290. if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { 
  291. $this->message[$pos]['xattrs']['!'] = null; 
  292. } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { 
  293. if (isset($this->message[$pos]['type'])) { 
  294. $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  295. } else { 
  296. $parent = $this->message[$pos]['parent']; 
  297. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  298. $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  299. } else { 
  300. $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata']; 
  301. $this->message[$pos]['result'] = $this->message[$pos]['xattrs']; 
  302. // set value of simpleType (or nil complexType) 
  303. } else { 
  304. //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']); 
  305. if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { 
  306. $this->message[$pos]['xattrs']['!'] = null; 
  307. } elseif (isset($this->message[$pos]['type'])) { 
  308. $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  309. } else { 
  310. $parent = $this->message[$pos]['parent']; 
  311. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  312. $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  313. } else { 
  314. $this->message[$pos]['result'] = $this->message[$pos]['cdata']; 
  315.  
  316. /** add value to parent's result, if parent is struct/array 
  317. $parent = $this->message[$pos]['parent']; 
  318. if($this->message[$parent]['type'] != 'map') { 
  319. if(strtolower($this->message[$parent]['type']) == 'array') { 
  320. $this->message[$parent]['result'][] = $this->message[$pos]['result']; 
  321. } else { 
  322. $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result']; 
  323. */ 
  324.  
  325. // for doclit 
  326. if($this->status == 'header') { 
  327. if ($this->root_header != $pos) { 
  328. $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>"; 
  329. } elseif($pos >= $this->root_struct) { 
  330. $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>"; 
  331. // switch status 
  332. if ($pos == $this->root_struct) { 
  333. $this->status = 'body'; 
  334. $this->root_struct_namespace = $this->message[$pos]['namespace']; 
  335. } elseif ($pos == $this->root_header) { 
  336. $this->status = 'envelope'; 
  337. } elseif ($name == 'Body' && $this->status == 'body') { 
  338. $this->status = 'envelope'; 
  339. } elseif ($name == 'Header' && $this->status == 'header') { // will never happen 
  340. $this->status = 'envelope'; 
  341. } elseif ($name == 'Envelope' && $this->status == 'envelope') { 
  342. $this->status = ''; 
  343. // set parent back to my parent 
  344. $this->parent = $this->message[$pos]['parent']; 
  345.  
  346. /** 
  347. * element content handler 
  348. * @param resource $parser XML parser object 
  349. * @param string $data element content 
  350. * @access private 
  351. */ 
  352. function character_data($parser, $data) { 
  353. $pos = $this->depth_array[$this->depth]; 
  354. if ($this->xml_encoding=='UTF-8') { 
  355. // TODO: add an option to disable this for folks who want 
  356. // raw UTF-8 that, e.g., might not map to iso-8859-1 
  357. // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1"); 
  358. if($this->decode_utf8) { 
  359. $data = utf8_decode($data); 
  360. $this->message[$pos]['cdata'] .= $data; 
  361. // for doclit 
  362. if($this->status == 'header') { 
  363. $this->responseHeaders .= $data; 
  364. } else { 
  365. $this->document .= $data; 
  366.  
  367. /** 
  368. * get the parsed message (SOAP Body) 
  369. * @return mixed 
  370. * @access public 
  371. * @deprecated use get_soapbody instead 
  372. */ 
  373. function get_response() { 
  374. return $this->soapresponse; 
  375.  
  376. /** 
  377. * get the parsed SOAP Body (NULL if there was none) 
  378. * @return mixed 
  379. * @access public 
  380. */ 
  381. function get_soapbody() { 
  382. return $this->soapresponse; 
  383.  
  384. /** 
  385. * get the parsed SOAP Header (NULL if there was none) 
  386. * @return mixed 
  387. * @access public 
  388. */ 
  389. function get_soapheader() { 
  390. return $this->soapheader; 
  391.  
  392. /** 
  393. * get the unparsed SOAP Header 
  394. * @return string XML or empty if no Header 
  395. * @access public 
  396. */ 
  397. function getHeaders() { 
  398. return $this->responseHeaders; 
  399.  
  400. /** 
  401. * decodes simple types into PHP variables 
  402. * @param string $value value to decode 
  403. * @param string $type XML type to decode 
  404. * @param string $typens XML type namespace to decode 
  405. * @return mixed PHP value 
  406. * @access private 
  407. */ 
  408. function decodeSimple($value, $type, $typens) { 
  409. // TODO: use the namespace! 
  410. if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') { 
  411. return (string) $value; 
  412. if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') { 
  413. return (int) $value; 
  414. if ($type == 'float' || $type == 'double' || $type == 'decimal') { 
  415. return (double) $value; 
  416. if ($type == 'boolean') { 
  417. if (strtolower($value) == 'false' || strtolower($value) == 'f') { 
  418. return false; 
  419. return (boolean) $value; 
  420. if ($type == 'base64' || $type == 'base64Binary') { 
  421. $this->debug('Decode base64 value'); 
  422. return base64_decode($value); 
  423. // obscure numeric types 
  424. if ($type == 'nonPositiveInteger' || $type == 'negativeInteger' 
  425. || $type == 'nonNegativeInteger' || $type == 'positiveInteger' 
  426. || $type == 'unsignedInt' 
  427. || $type == 'unsignedShort' || $type == 'unsignedByte') { 
  428. return (int) $value; 
  429. // bogus: parser treats array with no elements as a simple type 
  430. if ($type == 'array') { 
  431. return array(); 
  432. // everything else 
  433. return (string) $value; 
  434.  
  435. /** 
  436. * builds response structures for compound values (arrays/structs) 
  437. * and scalars 
  438. * @param integer $pos position in node tree 
  439. * @return mixed PHP value 
  440. * @access private 
  441. */ 
  442. function buildVal($pos) { 
  443. if(!isset($this->message[$pos]['type'])) { 
  444. $this->message[$pos]['type'] = ''; 
  445. $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']); 
  446. // if there are children... 
  447. if($this->message[$pos]['children'] != '') { 
  448. $this->debug('in buildVal, there are children'); 
  449. $children = explode('|', $this->message[$pos]['children']); 
  450. array_shift($children); // knock off empty 
  451. // md array 
  452. if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') { 
  453. $r=0; // rowcount 
  454. $c=0; // colcount 
  455. foreach($children as $child_pos) { 
  456. $this->debug("in buildVal, got an MD array element: $r, $c"); 
  457. $params[$r][] = $this->message[$child_pos]['result']; 
  458. $c++; 
  459. if($c == $this->message[$pos]['arrayCols']) { 
  460. $c = 0; 
  461. $r++; 
  462. // array 
  463. } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') { 
  464. $this->debug('in buildVal, adding array '.$this->message[$pos]['name']); 
  465. foreach($children as $child_pos) { 
  466. $params[] = &$this->message[$child_pos]['result']; 
  467. // apache Map type: java hashtable 
  468. } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { 
  469. $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']); 
  470. foreach($children as $child_pos) { 
  471. $kv = explode("|", $this->message[$child_pos]['children']); 
  472. $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result']; 
  473. // generic compound type 
  474. //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') { 
  475. } else { 
  476. // Apache Vector type: treat as an array 
  477. $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']); 
  478. if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { 
  479. $notstruct = 1; 
  480. } else { 
  481. $notstruct = 0; 
  482. // 
  483. foreach($children as $child_pos) { 
  484. if($notstruct) { 
  485. $params[] = &$this->message[$child_pos]['result']; 
  486. } else { 
  487. if (isset($params[$this->message[$child_pos]['name']])) { 
  488. // de-serialize repeated element name into an array 
  489. if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) { 
  490. $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]); 
  491. $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result']; 
  492. } else { 
  493. $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result']; 
  494. if (isset($this->message[$pos]['xattrs'])) { 
  495. $this->debug('in buildVal, handling attributes'); 
  496. foreach ($this->message[$pos]['xattrs'] as $n => $v) { 
  497. $params[$n] = $v; 
  498. // handle simpleContent 
  499. if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { 
  500. $this->debug('in buildVal, handling simpleContent'); 
  501. if (isset($this->message[$pos]['type'])) { 
  502. $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  503. } else { 
  504. $parent = $this->message[$pos]['parent']; 
  505. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  506. $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  507. } else { 
  508. $params['!'] = $this->message[$pos]['cdata']; 
  509. $ret = is_array($params) ? $params : array(); 
  510. $this->debug('in buildVal, return:'); 
  511. $this->appendDebug($this->varDump($ret)); 
  512. return $ret; 
  513. } else { 
  514. $this->debug('in buildVal, no children, building scalar'); 
  515. $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : ''; 
  516. if (isset($this->message[$pos]['type'])) { 
  517. $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  518. $this->debug("in buildVal, return: $ret"); 
  519. return $ret; 
  520. $parent = $this->message[$pos]['parent']; 
  521. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  522. $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  523. $this->debug("in buildVal, return: $ret"); 
  524. return $ret; 
  525. $ret = $this->message[$pos]['cdata']; 
  526. $this->debug("in buildVal, return: $ret"); 
  527. return $ret; 
/lib/nusoap.php  
  1. class nusoap_parser extends nusoap_base { 
  2.  
  3. var $xml = ''; 
  4. var $xml_encoding = ''; 
  5. var $method = ''; 
  6. var $root_struct = ''; 
  7. var $root_struct_name = ''; 
  8. var $root_struct_namespace = ''; 
  9. var $root_header = ''; 
  10. var $document = ''; // incoming SOAP body (text) 
  11. // determines where in the message we are (envelope, header, body, method) 
  12. var $status = ''; 
  13. var $position = 0; 
  14. var $depth = 0; 
  15. var $default_namespace = ''; 
  16. var $namespaces = array(); 
  17. var $message = array(); 
  18. var $parent = ''; 
  19. var $fault = false; 
  20. var $fault_code = ''; 
  21. var $fault_str = ''; 
  22. var $fault_detail = ''; 
  23. var $depth_array = array(); 
  24. var $debug_flag = true; 
  25. var $soapresponse = NULL; // parsed SOAP Body 
  26. var $soapheader = NULL; // parsed SOAP Header 
  27. var $responseHeaders = ''; // incoming SOAP headers (text) 
  28. var $body_position = 0; 
  29. // for multiref parsing: 
  30. // array of id => pos 
  31. var $ids = array(); 
  32. // array of id => hrefs => pos 
  33. var $multirefs = array(); 
  34. // toggle for auto-decoding element content 
  35. var $decode_utf8 = true; 
  36.  
  37. /** 
  38. * constructor that actually does the parsing 
  39. * @param string $xml SOAP message 
  40. * @param string $encoding character encoding scheme of message 
  41. * @param string $method method for which XML is parsed (unused?) 
  42. * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1 
  43. * @access public 
  44. */ 
  45. function nusoap_parser($xml, $encoding='UTF-8', $method='', $decode_utf8=true) { 
  46. parent::nusoap_base(); 
  47. $this->xml = $xml; 
  48. $this->xml_encoding = $encoding; 
  49. $this->method = $method; 
  50. $this->decode_utf8 = $decode_utf8; 
  51.  
  52. // Check whether content has been read. 
  53. if(!empty($xml)) { 
  54. // Check XML encoding 
  55. $pos_xml = strpos($xml, '<?xml'); 
  56. if ($pos_xml !== FALSE) { 
  57. $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1); 
  58. if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) { 
  59. $xml_encoding = $res[1]; 
  60. if (strtoupper($xml_encoding) != $encoding) { 
  61. $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'"; 
  62. $this->debug($err); 
  63. if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') { 
  64. $this->setError($err); 
  65. return; 
  66. // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed 
  67. } else { 
  68. $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration'); 
  69. } else { 
  70. $this->debug('No encoding specified in XML declaration'); 
  71. } else { 
  72. $this->debug('No XML declaration'); 
  73. $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding); 
  74. // Create an XML parser - why not xml_parser_create_ns? 
  75. $this->parser = xml_parser_create($this->xml_encoding); 
  76. // Set the options for parsing the XML data. 
  77. //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); 
  78. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
  79. xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding); 
  80. // Set the object for the parser. 
  81. xml_set_object($this->parser, $this); 
  82. // Set the element handlers for the parser. 
  83. xml_set_element_handler($this->parser, 'start_element', 'end_element'); 
  84. xml_set_character_data_handler($this->parser, 'character_data'); 
  85.  
  86. // Parse the XML file. 
  87. if(!xml_parse($this->parser, $xml, true)) { 
  88. // Display an error message. 
  89. $err = sprintf('XML error parsing SOAP payload on line %d: %s',  
  90. xml_get_current_line_number($this->parser),  
  91. xml_error_string(xml_get_error_code($this->parser))); 
  92. $this->debug($err); 
  93. $this->debug("XML payload:\n" . $xml); 
  94. $this->setError($err); 
  95. } else { 
  96. $this->debug('in nusoap_parser ctor, message:'); 
  97. $this->appendDebug($this->varDump($this->message)); 
  98. $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name); 
  99. // get final value 
  100. $this->soapresponse = $this->message[$this->root_struct]['result']; 
  101. // get header value 
  102. if($this->root_header != '' && isset($this->message[$this->root_header]['result'])) { 
  103. $this->soapheader = $this->message[$this->root_header]['result']; 
  104. // resolve hrefs/ids 
  105. if(sizeof($this->multirefs) > 0) { 
  106. foreach($this->multirefs as $id => $hrefs) { 
  107. $this->debug('resolving multirefs for id: '.$id); 
  108. $idVal = $this->buildVal($this->ids[$id]); 
  109. if (is_array($idVal) && isset($idVal['!id'])) { 
  110. unset($idVal['!id']); 
  111. foreach($hrefs as $refPos => $ref) { 
  112. $this->debug('resolving href at pos '.$refPos); 
  113. $this->multirefs[$id][$refPos] = $idVal; 
  114. xml_parser_free($this->parser); 
  115. } else { 
  116. $this->debug('xml was empty, didn\'t parse!'); 
  117. $this->setError('xml was empty, didn\'t parse!'); 
  118.  
  119. /** 
  120. * start-element handler 
  121. * @param resource $parser XML parser object 
  122. * @param string $name element name 
  123. * @param array $attrs associative array of attributes 
  124. * @access private 
  125. */ 
  126. function start_element($parser, $name, $attrs) { 
  127. // position in a total number of elements, starting from 0 
  128. // update class level pos 
  129. $pos = $this->position++; 
  130. // and set mine 
  131. $this->message[$pos] = array('pos' => $pos, 'children'=>'', 'cdata'=>''); 
  132. // depth = how many levels removed from root? 
  133. // set mine as current global depth and increment global depth value 
  134. $this->message[$pos]['depth'] = $this->depth++; 
  135.  
  136. // else add self as child to whoever the current parent is 
  137. if($pos != 0) { 
  138. $this->message[$this->parent]['children'] .= '|'.$pos; 
  139. // set my parent 
  140. $this->message[$pos]['parent'] = $this->parent; 
  141. // set self as current parent 
  142. $this->parent = $pos; 
  143. // set self as current value for this depth 
  144. $this->depth_array[$this->depth] = $pos; 
  145. // get element prefix 
  146. if(strpos($name, ':')) { 
  147. // get ns prefix 
  148. $prefix = substr($name, 0, strpos($name, ':')); 
  149. // get unqualified name 
  150. $name = substr(strstr($name, ':'), 1); 
  151. // set status 
  152. if ($name == 'Envelope' && $this->status == '') { 
  153. $this->status = 'envelope'; 
  154. } elseif ($name == 'Header' && $this->status == 'envelope') { 
  155. $this->root_header = $pos; 
  156. $this->status = 'header'; 
  157. } elseif ($name == 'Body' && $this->status == 'envelope') { 
  158. $this->status = 'body'; 
  159. $this->body_position = $pos; 
  160. // set method 
  161. } elseif($this->status == 'body' && $pos == ($this->body_position+1)) { 
  162. $this->status = 'method'; 
  163. $this->root_struct_name = $name; 
  164. $this->root_struct = $pos; 
  165. $this->message[$pos]['type'] = 'struct'; 
  166. $this->debug("found root struct $this->root_struct_name, pos $this->root_struct"); 
  167. // set my status 
  168. $this->message[$pos]['status'] = $this->status; 
  169. // set name 
  170. $this->message[$pos]['name'] = htmlspecialchars($name); 
  171. // set attrs 
  172. $this->message[$pos]['attrs'] = $attrs; 
  173.  
  174. // loop through atts, logging ns and type declarations 
  175. $attstr = ''; 
  176. foreach($attrs as $key => $value) { 
  177. $key_prefix = $this->getPrefix($key); 
  178. $key_localpart = $this->getLocalPart($key); 
  179. // if ns declarations, add to class level array of valid namespaces 
  180. if($key_prefix == 'xmlns') { 
  181. if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) { 
  182. $this->XMLSchemaVersion = $value; 
  183. $this->namespaces['xsd'] = $this->XMLSchemaVersion; 
  184. $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance'; 
  185. $this->namespaces[$key_localpart] = $value; 
  186. // set method namespace 
  187. if($name == $this->root_struct_name) { 
  188. $this->methodNamespace = $value; 
  189. // if it's a type declaration, set type 
  190. } elseif($key_localpart == 'type') { 
  191. if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') { 
  192. // do nothing: already processed arrayType 
  193. } else { 
  194. $value_prefix = $this->getPrefix($value); 
  195. $value_localpart = $this->getLocalPart($value); 
  196. $this->message[$pos]['type'] = $value_localpart; 
  197. $this->message[$pos]['typePrefix'] = $value_prefix; 
  198. if(isset($this->namespaces[$value_prefix])) { 
  199. $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix]; 
  200. } else if(isset($attrs['xmlns:'.$value_prefix])) { 
  201. $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix]; 
  202. // should do something here with the namespace of specified type? 
  203. } elseif($key_localpart == 'arrayType') { 
  204. $this->message[$pos]['type'] = 'array'; 
  205. /** do arrayType ereg here 
  206. [1] arrayTypeValue ::= atype asize 
  207. [2] atype ::= QName rank* 
  208. [3] rank ::= '[' (', ')* ']' 
  209. [4] asize ::= '[' length~ ']' 
  210. [5] length ::= nextDimension* Digit+ 
  211. [6] nextDimension ::= Digit+ ', ' 
  212. */ 
  213. $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+), ?([0-9]*)\]/'; 
  214. if(preg_match($expr, $value, $regs)) { 
  215. $this->message[$pos]['typePrefix'] = $regs[1]; 
  216. $this->message[$pos]['arrayTypePrefix'] = $regs[1]; 
  217. if (isset($this->namespaces[$regs[1]])) { 
  218. $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]]; 
  219. } else if (isset($attrs['xmlns:'.$regs[1]])) { 
  220. $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]]; 
  221. $this->message[$pos]['arrayType'] = $regs[2]; 
  222. $this->message[$pos]['arraySize'] = $regs[3]; 
  223. $this->message[$pos]['arrayCols'] = $regs[4]; 
  224. // specifies nil value (or not) 
  225. } elseif ($key_localpart == 'nil') { 
  226. $this->message[$pos]['nil'] = ($value == 'true' || $value == '1'); 
  227. // some other attribute 
  228. } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') { 
  229. $this->message[$pos]['xattrs']['!' . $key] = $value; 
  230.  
  231. if ($key == 'xmlns') { 
  232. $this->default_namespace = $value; 
  233. // log id 
  234. if($key == 'id') { 
  235. $this->ids[$value] = $pos; 
  236. // root 
  237. if($key_localpart == 'root' && $value == 1) { 
  238. $this->status = 'method'; 
  239. $this->root_struct_name = $name; 
  240. $this->root_struct = $pos; 
  241. $this->debug("found root struct $this->root_struct_name, pos $pos"); 
  242. // for doclit 
  243. $attstr .= " $key=\"$value\""; 
  244. // get namespace - must be done after namespace atts are processed 
  245. if(isset($prefix)) { 
  246. $this->message[$pos]['namespace'] = $this->namespaces[$prefix]; 
  247. $this->default_namespace = $this->namespaces[$prefix]; 
  248. } else { 
  249. $this->message[$pos]['namespace'] = $this->default_namespace; 
  250. if($this->status == 'header') { 
  251. if ($this->root_header != $pos) { 
  252. $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; 
  253. } elseif($this->root_struct_name != '') { 
  254. $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; 
  255.  
  256. /** 
  257. * end-element handler 
  258. * @param resource $parser XML parser object 
  259. * @param string $name element name 
  260. * @access private 
  261. */ 
  262. function end_element($parser, $name) { 
  263. // position of current element is equal to the last value left in depth_array for my depth 
  264. $pos = $this->depth_array[$this->depth--]; 
  265.  
  266. // get element prefix 
  267. if(strpos($name, ':')) { 
  268. // get ns prefix 
  269. $prefix = substr($name, 0, strpos($name, ':')); 
  270. // get unqualified name 
  271. $name = substr(strstr($name, ':'), 1); 
  272.  
  273. // build to native type 
  274. if(isset($this->body_position) && $pos > $this->body_position) { 
  275. // deal w/ multirefs 
  276. if(isset($this->message[$pos]['attrs']['href'])) { 
  277. // get id 
  278. $id = substr($this->message[$pos]['attrs']['href'], 1); 
  279. // add placeholder to href array 
  280. $this->multirefs[$id][$pos] = 'placeholder'; 
  281. // add set a reference to it as the result value 
  282. $this->message[$pos]['result'] =& $this->multirefs[$id][$pos]; 
  283. // build complexType values 
  284. } elseif($this->message[$pos]['children'] != '') { 
  285. // if result has already been generated (struct/array) 
  286. if(!isset($this->message[$pos]['result'])) { 
  287. $this->message[$pos]['result'] = $this->buildVal($pos); 
  288. // build complexType values of attributes and possibly simpleContent 
  289. } elseif (isset($this->message[$pos]['xattrs'])) { 
  290. if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { 
  291. $this->message[$pos]['xattrs']['!'] = null; 
  292. } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { 
  293. if (isset($this->message[$pos]['type'])) { 
  294. $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  295. } else { 
  296. $parent = $this->message[$pos]['parent']; 
  297. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  298. $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  299. } else { 
  300. $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata']; 
  301. $this->message[$pos]['result'] = $this->message[$pos]['xattrs']; 
  302. // set value of simpleType (or nil complexType) 
  303. } else { 
  304. //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']); 
  305. if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { 
  306. $this->message[$pos]['xattrs']['!'] = null; 
  307. } elseif (isset($this->message[$pos]['type'])) { 
  308. $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  309. } else { 
  310. $parent = $this->message[$pos]['parent']; 
  311. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  312. $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  313. } else { 
  314. $this->message[$pos]['result'] = $this->message[$pos]['cdata']; 
  315.  
  316. /** add value to parent's result, if parent is struct/array 
  317. $parent = $this->message[$pos]['parent']; 
  318. if($this->message[$parent]['type'] != 'map') { 
  319. if(strtolower($this->message[$parent]['type']) == 'array') { 
  320. $this->message[$parent]['result'][] = $this->message[$pos]['result']; 
  321. } else { 
  322. $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result']; 
  323. */ 
  324.  
  325. // for doclit 
  326. if($this->status == 'header') { 
  327. if ($this->root_header != $pos) { 
  328. $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>"; 
  329. } elseif($pos >= $this->root_struct) { 
  330. $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>"; 
  331. // switch status 
  332. if ($pos == $this->root_struct) { 
  333. $this->status = 'body'; 
  334. $this->root_struct_namespace = $this->message[$pos]['namespace']; 
  335. } elseif ($pos == $this->root_header) { 
  336. $this->status = 'envelope'; 
  337. } elseif ($name == 'Body' && $this->status == 'body') { 
  338. $this->status = 'envelope'; 
  339. } elseif ($name == 'Header' && $this->status == 'header') { // will never happen 
  340. $this->status = 'envelope'; 
  341. } elseif ($name == 'Envelope' && $this->status == 'envelope') { 
  342. $this->status = ''; 
  343. // set parent back to my parent 
  344. $this->parent = $this->message[$pos]['parent']; 
  345.  
  346. /** 
  347. * element content handler 
  348. * @param resource $parser XML parser object 
  349. * @param string $data element content 
  350. * @access private 
  351. */ 
  352. function character_data($parser, $data) { 
  353. $pos = $this->depth_array[$this->depth]; 
  354. if ($this->xml_encoding=='UTF-8') { 
  355. // TODO: add an option to disable this for folks who want 
  356. // raw UTF-8 that, e.g., might not map to iso-8859-1 
  357. // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1"); 
  358. if($this->decode_utf8) { 
  359. $data = utf8_decode($data); 
  360. $this->message[$pos]['cdata'] .= $data; 
  361. // for doclit 
  362. if($this->status == 'header') { 
  363. $this->responseHeaders .= $data; 
  364. } else { 
  365. $this->document .= $data; 
  366.  
  367. /** 
  368. * get the parsed message (SOAP Body) 
  369. * @return mixed 
  370. * @access public 
  371. * @deprecated use get_soapbody instead 
  372. */ 
  373. function get_response() { 
  374. return $this->soapresponse; 
  375.  
  376. /** 
  377. * get the parsed SOAP Body (NULL if there was none) 
  378. * @return mixed 
  379. * @access public 
  380. */ 
  381. function get_soapbody() { 
  382. return $this->soapresponse; 
  383.  
  384. /** 
  385. * get the parsed SOAP Header (NULL if there was none) 
  386. * @return mixed 
  387. * @access public 
  388. */ 
  389. function get_soapheader() { 
  390. return $this->soapheader; 
  391.  
  392. /** 
  393. * get the unparsed SOAP Header 
  394. * @return string XML or empty if no Header 
  395. * @access public 
  396. */ 
  397. function getHeaders() { 
  398. return $this->responseHeaders; 
  399.  
  400. /** 
  401. * decodes simple types into PHP variables 
  402. * @param string $value value to decode 
  403. * @param string $type XML type to decode 
  404. * @param string $typens XML type namespace to decode 
  405. * @return mixed PHP value 
  406. * @access private 
  407. */ 
  408. function decodeSimple($value, $type, $typens) { 
  409. // TODO: use the namespace! 
  410. if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') { 
  411. return (string) $value; 
  412. if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') { 
  413. return (int) $value; 
  414. if ($type == 'float' || $type == 'double' || $type == 'decimal') { 
  415. return (double) $value; 
  416. if ($type == 'boolean') { 
  417. if (strtolower($value) == 'false' || strtolower($value) == 'f') { 
  418. return false; 
  419. return (boolean) $value; 
  420. if ($type == 'base64' || $type == 'base64Binary') { 
  421. $this->debug('Decode base64 value'); 
  422. return base64_decode($value); 
  423. // obscure numeric types 
  424. if ($type == 'nonPositiveInteger' || $type == 'negativeInteger' 
  425. || $type == 'nonNegativeInteger' || $type == 'positiveInteger' 
  426. || $type == 'unsignedInt' 
  427. || $type == 'unsignedShort' || $type == 'unsignedByte') { 
  428. return (int) $value; 
  429. // bogus: parser treats array with no elements as a simple type 
  430. if ($type == 'array') { 
  431. return array(); 
  432. // everything else 
  433. return (string) $value; 
  434.  
  435. /** 
  436. * builds response structures for compound values (arrays/structs) 
  437. * and scalars 
  438. * @param integer $pos position in node tree 
  439. * @return mixed PHP value 
  440. * @access private 
  441. */ 
  442. function buildVal($pos) { 
  443. if(!isset($this->message[$pos]['type'])) { 
  444. $this->message[$pos]['type'] = ''; 
  445. $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']); 
  446. // if there are children... 
  447. if($this->message[$pos]['children'] != '') { 
  448. $this->debug('in buildVal, there are children'); 
  449. $children = explode('|', $this->message[$pos]['children']); 
  450. array_shift($children); // knock off empty 
  451. // md array 
  452. if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') { 
  453. $r=0; // rowcount 
  454. $c=0; // colcount 
  455. foreach($children as $child_pos) { 
  456. $this->debug("in buildVal, got an MD array element: $r, $c"); 
  457. $params[$r][] = $this->message[$child_pos]['result']; 
  458. $c++; 
  459. if($c == $this->message[$pos]['arrayCols']) { 
  460. $c = 0; 
  461. $r++; 
  462. // array 
  463. } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') { 
  464. $this->debug('in buildVal, adding array '.$this->message[$pos]['name']); 
  465. foreach($children as $child_pos) { 
  466. $params[] = &$this->message[$child_pos]['result']; 
  467. // apache Map type: java hashtable 
  468. } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { 
  469. $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']); 
  470. foreach($children as $child_pos) { 
  471. $kv = explode("|", $this->message[$child_pos]['children']); 
  472. $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result']; 
  473. // generic compound type 
  474. //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') { 
  475. } else { 
  476. // Apache Vector type: treat as an array 
  477. $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']); 
  478. if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { 
  479. $notstruct = 1; 
  480. } else { 
  481. $notstruct = 0; 
  482. // 
  483. foreach($children as $child_pos) { 
  484. if($notstruct) { 
  485. $params[] = &$this->message[$child_pos]['result']; 
  486. } else { 
  487. if (isset($params[$this->message[$child_pos]['name']])) { 
  488. // de-serialize repeated element name into an array 
  489. if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) { 
  490. $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]); 
  491. $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result']; 
  492. } else { 
  493. $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result']; 
  494. if (isset($this->message[$pos]['xattrs'])) { 
  495. $this->debug('in buildVal, handling attributes'); 
  496. foreach ($this->message[$pos]['xattrs'] as $n => $v) { 
  497. $params[$n] = $v; 
  498. // handle simpleContent 
  499. if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { 
  500. $this->debug('in buildVal, handling simpleContent'); 
  501. if (isset($this->message[$pos]['type'])) { 
  502. $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  503. } else { 
  504. $parent = $this->message[$pos]['parent']; 
  505. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  506. $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  507. } else { 
  508. $params['!'] = $this->message[$pos]['cdata']; 
  509. $ret = is_array($params) ? $params : array(); 
  510. $this->debug('in buildVal, return:'); 
  511. $this->appendDebug($this->varDump($ret)); 
  512. return $ret; 
  513. } else { 
  514. $this->debug('in buildVal, no children, building scalar'); 
  515. $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : ''; 
  516. if (isset($this->message[$pos]['type'])) { 
  517. $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); 
  518. $this->debug("in buildVal, return: $ret"); 
  519. return $ret; 
  520. $parent = $this->message[$pos]['parent']; 
  521. if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { 
  522. $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); 
  523. $this->debug("in buildVal, return: $ret"); 
  524. return $ret; 
  525. $ret = $this->message[$pos]['cdata']; 
  526. $this->debug("in buildVal, return: $ret"); 
  527. return $ret;