nusoap_server

Nusoap_server allows the user to create a SOAP server that is capable of receiving messages and returning responses.

Defined (2)

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

/lib/class.soap_server.php  
  1. class nusoap_server extends nusoap_base { 
  2. /** 
  3. * HTTP headers of request 
  4. * @var array 
  5. * @access private 
  6. */ 
  7. var $headers = array(); 
  8. /** 
  9. * HTTP request 
  10. * @var string 
  11. * @access private 
  12. */ 
  13. var $request = ''; 
  14. /** 
  15. * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text) 
  16. * @var string 
  17. * @access public 
  18. */ 
  19. var $requestHeaders = ''; 
  20. /** 
  21. * SOAP Headers from request (parsed) 
  22. * @var mixed 
  23. * @access public 
  24. */ 
  25. var $requestHeader = NULL; 
  26. /** 
  27. * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text) 
  28. * @var string 
  29. * @access public 
  30. */ 
  31. var $document = ''; 
  32. /** 
  33. * SOAP payload for request (text) 
  34. * @var string 
  35. * @access public 
  36. */ 
  37. var $requestSOAP = ''; 
  38. /** 
  39. * requested method namespace URI 
  40. * @var string 
  41. * @access private 
  42. */ 
  43. var $methodURI = ''; 
  44. /** 
  45. * name of method requested 
  46. * @var string 
  47. * @access private 
  48. */ 
  49. var $methodname = ''; 
  50. /** 
  51. * method parameters from request 
  52. * @var array 
  53. * @access private 
  54. */ 
  55. var $methodparams = array(); 
  56. /** 
  57. * SOAP Action from request 
  58. * @var string 
  59. * @access private 
  60. */ 
  61. var $SOAPAction = ''; 
  62. /** 
  63. * character set encoding of incoming (request) messages 
  64. * @var string 
  65. * @access public 
  66. */ 
  67. var $xml_encoding = ''; 
  68. /** 
  69. * toggles whether the parser decodes element content w/ utf8_decode() 
  70. * @var boolean 
  71. * @access public 
  72. */ 
  73. var $decode_utf8 = true; 
  74.  
  75. /** 
  76. * HTTP headers of response 
  77. * @var array 
  78. * @access public 
  79. */ 
  80. var $outgoing_headers = array(); 
  81. /** 
  82. * HTTP response 
  83. * @var string 
  84. * @access private 
  85. */ 
  86. var $response = ''; 
  87. /** 
  88. * SOAP headers for response (text or array of soapval or associative array) 
  89. * @var mixed 
  90. * @access public 
  91. */ 
  92. var $responseHeaders = ''; 
  93. /** 
  94. * SOAP payload for response (text) 
  95. * @var string 
  96. * @access private 
  97. */ 
  98. var $responseSOAP = ''; 
  99. /** 
  100. * method return value to place in response 
  101. * @var mixed 
  102. * @access private 
  103. */ 
  104. var $methodreturn = false; 
  105. /** 
  106. * whether $methodreturn is a string of literal XML 
  107. * @var boolean 
  108. * @access public 
  109. */ 
  110. var $methodreturnisliteralxml = false; 
  111. /** 
  112. * SOAP fault for response (or false) 
  113. * @var mixed 
  114. * @access private 
  115. */ 
  116. var $fault = false; 
  117. /** 
  118. * text indication of result (for debugging) 
  119. * @var string 
  120. * @access private 
  121. */ 
  122. var $result = 'successful'; 
  123.  
  124. /** 
  125. * assoc array of operations => opData; operations are added by the register() 
  126. * method or by parsing an external WSDL definition 
  127. * @var array 
  128. * @access private 
  129. */ 
  130. var $operations = array(); 
  131. /** 
  132. * wsdl instance (if one) 
  133. * @var mixed 
  134. * @access private 
  135. */ 
  136. var $wsdl = false; 
  137. /** 
  138. * URL for WSDL (if one) 
  139. * @var mixed 
  140. * @access private 
  141. */ 
  142. var $externalWSDLURL = false; 
  143. /** 
  144. * whether to append debug to response as XML comment 
  145. * @var boolean 
  146. * @access public 
  147. */ 
  148. var $debug_flag = false; 
  149.  
  150.  
  151. /** 
  152. * constructor 
  153. * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. 
  154. * @param mixed $wsdl file path or URL (string), or wsdl instance (object) 
  155. * @access public 
  156. */ 
  157. function nusoap_server($wsdl=false) { 
  158. parent::nusoap_base(); 
  159. // turn on debugging? 
  160. global $debug; 
  161. global $HTTP_SERVER_VARS; 
  162.  
  163. if (isset($_SERVER)) { 
  164. $this->debug("_SERVER is defined:"); 
  165. $this->appendDebug($this->varDump($_SERVER)); 
  166. } elseif (isset($HTTP_SERVER_VARS)) { 
  167. $this->debug("HTTP_SERVER_VARS is defined:"); 
  168. $this->appendDebug($this->varDump($HTTP_SERVER_VARS)); 
  169. } else { 
  170. $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined."); 
  171.  
  172. if (isset($debug)) { 
  173. $this->debug("In nusoap_server, set debug_flag=$debug based on global flag"); 
  174. $this->debug_flag = $debug; 
  175. } elseif (isset($_SERVER['QUERY_STRING'])) { 
  176. $qs = explode('&', $_SERVER['QUERY_STRING']); 
  177. foreach ($qs as $v) { 
  178. if (substr($v, 0, 6) == 'debug=') { 
  179. $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1"); 
  180. $this->debug_flag = substr($v, 6); 
  181. } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { 
  182. $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']); 
  183. foreach ($qs as $v) { 
  184. if (substr($v, 0, 6) == 'debug=') { 
  185. $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2"); 
  186. $this->debug_flag = substr($v, 6); 
  187.  
  188. // wsdl 
  189. if($wsdl) { 
  190. $this->debug("In nusoap_server, WSDL is specified"); 
  191. if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) { 
  192. $this->wsdl = $wsdl; 
  193. $this->externalWSDLURL = $this->wsdl->wsdl; 
  194. $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL); 
  195. } else { 
  196. $this->debug('Create wsdl from ' . $wsdl); 
  197. $this->wsdl = new wsdl($wsdl); 
  198. $this->externalWSDLURL = $wsdl; 
  199. $this->appendDebug($this->wsdl->getDebug()); 
  200. $this->wsdl->clearDebug(); 
  201. if($err = $this->wsdl->getError()) { 
  202. die('WSDL ERROR: '.$err); 
  203.  
  204. /** 
  205. * processes request and returns response 
  206. * @param string $data usually is the value of $HTTP_RAW_POST_DATA 
  207. * @access public 
  208. */ 
  209. function service($data) { 
  210. global $HTTP_SERVER_VARS; 
  211.  
  212. if (isset($_SERVER['REQUEST_METHOD'])) { 
  213. $rm = $_SERVER['REQUEST_METHOD']; 
  214. } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) { 
  215. $rm = $HTTP_SERVER_VARS['REQUEST_METHOD']; 
  216. } else { 
  217. $rm = ''; 
  218.  
  219. if (isset($_SERVER['QUERY_STRING'])) { 
  220. $qs = $_SERVER['QUERY_STRING']; 
  221. } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { 
  222. $qs = $HTTP_SERVER_VARS['QUERY_STRING']; 
  223. } else { 
  224. $qs = ''; 
  225. $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data)); 
  226.  
  227. if ($rm == 'POST') { 
  228. $this->debug("In service, invoke the request"); 
  229. $this->parse_request($data); 
  230. if (! $this->fault) { 
  231. $this->invoke_method(); 
  232. if (! $this->fault) { 
  233. $this->serialize_return(); 
  234. $this->send_response(); 
  235. } elseif (preg_match('/wsdl/', $qs) ) { 
  236. $this->debug("In service, this is a request for WSDL"); 
  237. if ($this->externalWSDLURL) { 
  238. if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL 
  239. $this->debug("In service, re-direct for WSDL"); 
  240. header('Location: '.$this->externalWSDLURL); 
  241. } else { // assume file 
  242. $this->debug("In service, use file passthru for WSDL"); 
  243. header("Content-Type: text/xml\r\n"); 
  244. $pos = strpos($this->externalWSDLURL, "file://"); 
  245. if ($pos === false) { 
  246. $filename = $this->externalWSDLURL; 
  247. } else { 
  248. $filename = substr($this->externalWSDLURL, $pos + 7); 
  249. $fp = fopen($this->externalWSDLURL, 'r'); 
  250. fpassthru($fp); 
  251. } elseif ($this->wsdl) { 
  252. $this->debug("In service, serialize WSDL"); 
  253. header("Content-Type: text/xml; charset=ISO-8859-1\r\n"); 
  254. print $this->wsdl->serialize($this->debug_flag); 
  255. if ($this->debug_flag) { 
  256. $this->debug('wsdl:'); 
  257. $this->appendDebug($this->varDump($this->wsdl)); 
  258. print $this->getDebugAsXMLComment(); 
  259. } else { 
  260. $this->debug("In service, there is no WSDL"); 
  261. header("Content-Type: text/html; charset=ISO-8859-1\r\n"); 
  262. print "This service does not provide WSDL"; 
  263. } elseif ($this->wsdl) { 
  264. $this->debug("In service, return Web description"); 
  265. print $this->wsdl->webDescription(); 
  266. } else { 
  267. $this->debug("In service, no Web description"); 
  268. header("Content-Type: text/html; charset=ISO-8859-1\r\n"); 
  269. print "This service does not provide a Web description"; 
  270.  
  271. /** 
  272. * parses HTTP request headers. 
  273. * The following fields are set by this function (when successful) 
  274. * headers 
  275. * request 
  276. * xml_encoding 
  277. * SOAPAction 
  278. * @access private 
  279. */ 
  280. function parse_http_headers() { 
  281. global $HTTP_SERVER_VARS; 
  282.  
  283. $this->request = ''; 
  284. $this->SOAPAction = ''; 
  285. if(function_exists('getallheaders')) { 
  286. $this->debug("In parse_http_headers, use getallheaders"); 
  287. $headers = getallheaders(); 
  288. foreach($headers as $k=>$v) { 
  289. $k = strtolower($k); 
  290. $this->headers[$k] = $v; 
  291. $this->request .= "$k: $v\r\n"; 
  292. $this->debug("$k: $v"); 
  293. // get SOAPAction header 
  294. if(isset($this->headers['soapaction'])) { 
  295. $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']); 
  296. // get the character encoding of the incoming request 
  297. if(isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) { 
  298. $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1)); 
  299. if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  300. $this->xml_encoding = strtoupper($enc); 
  301. } else { 
  302. $this->xml_encoding = 'US-ASCII'; 
  303. } else { 
  304. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  305. $this->xml_encoding = 'ISO-8859-1'; 
  306. } elseif(isset($_SERVER) && is_array($_SERVER)) { 
  307. $this->debug("In parse_http_headers, use _SERVER"); 
  308. foreach ($_SERVER as $k => $v) { 
  309. if (substr($k, 0, 5) == 'HTTP_') { 
  310. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 
  311. } else { 
  312. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 
  313. if ($k == 'soapaction') { 
  314. // get SOAPAction header 
  315. $k = 'SOAPAction'; 
  316. $v = str_replace('"', '', $v); 
  317. $v = str_replace('\\', '', $v); 
  318. $this->SOAPAction = $v; 
  319. } else if ($k == 'content-type') { 
  320. // get the character encoding of the incoming request 
  321. if (strpos($v, '=')) { 
  322. $enc = substr(strstr($v, '='), 1); 
  323. $enc = str_replace('"', '', $enc); 
  324. $enc = str_replace('\\', '', $enc); 
  325. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  326. $this->xml_encoding = strtoupper($enc); 
  327. } else { 
  328. $this->xml_encoding = 'US-ASCII'; 
  329. } else { 
  330. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  331. $this->xml_encoding = 'ISO-8859-1'; 
  332. $this->headers[$k] = $v; 
  333. $this->request .= "$k: $v\r\n"; 
  334. $this->debug("$k: $v"); 
  335. } elseif (is_array($HTTP_SERVER_VARS)) { 
  336. $this->debug("In parse_http_headers, use HTTP_SERVER_VARS"); 
  337. foreach ($HTTP_SERVER_VARS as $k => $v) { 
  338. if (substr($k, 0, 5) == 'HTTP_') { 
  339. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5)); 
  340. } else { 
  341. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k); 
  342. if ($k == 'soapaction') { 
  343. // get SOAPAction header 
  344. $k = 'SOAPAction'; 
  345. $v = str_replace('"', '', $v); 
  346. $v = str_replace('\\', '', $v); 
  347. $this->SOAPAction = $v; 
  348. } else if ($k == 'content-type') { 
  349. // get the character encoding of the incoming request 
  350. if (strpos($v, '=')) { 
  351. $enc = substr(strstr($v, '='), 1); 
  352. $enc = str_replace('"', '', $enc); 
  353. $enc = str_replace('\\', '', $enc); 
  354. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  355. $this->xml_encoding = strtoupper($enc); 
  356. } else { 
  357. $this->xml_encoding = 'US-ASCII'; 
  358. } else { 
  359. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  360. $this->xml_encoding = 'ISO-8859-1'; 
  361. $this->headers[$k] = $v; 
  362. $this->request .= "$k: $v\r\n"; 
  363. $this->debug("$k: $v"); 
  364. } else { 
  365. $this->debug("In parse_http_headers, HTTP headers not accessible"); 
  366. $this->setError("HTTP headers not accessible"); 
  367.  
  368. /** 
  369. * parses a request 
  370. * The following fields are set by this function (when successful) 
  371. * headers 
  372. * request 
  373. * xml_encoding 
  374. * SOAPAction 
  375. * request 
  376. * requestSOAP 
  377. * methodURI 
  378. * methodname 
  379. * methodparams 
  380. * requestHeaders 
  381. * document 
  382. * This sets the fault field on error 
  383. * @param string $data XML string 
  384. * @access private 
  385. */ 
  386. function parse_request($data='') { 
  387. $this->debug('entering parse_request()'); 
  388. $this->parse_http_headers(); 
  389. $this->debug('got character encoding: '.$this->xml_encoding); 
  390. // uncompress if necessary 
  391. if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') { 
  392. $this->debug('got content encoding: ' . $this->headers['content-encoding']); 
  393. if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') { 
  394. // if decoding works, use it. else assume data wasn't gzencoded 
  395. if (function_exists('gzuncompress')) { 
  396. if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) { 
  397. $data = $degzdata; 
  398. } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) { 
  399. $data = $degzdata; 
  400. } else { 
  401. $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data'); 
  402. return; 
  403. } else { 
  404. $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data'); 
  405. return; 
  406. $this->request .= "\r\n".$data; 
  407. $data = $this->parseRequest($this->headers, $data); 
  408. $this->requestSOAP = $data; 
  409. $this->debug('leaving parse_request'); 
  410.  
  411. /** 
  412. * invokes a PHP function for the requested SOAP method 
  413. * The following fields are set by this function (when successful) 
  414. * methodreturn 
  415. * Note that the PHP function that is called may also set the following 
  416. * fields to affect the response sent to the client 
  417. * responseHeaders 
  418. * outgoing_headers 
  419. * This sets the fault field on error 
  420. * @access private 
  421. */ 
  422. function invoke_method() { 
  423. $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction); 
  424.  
  425. // 
  426. // if you are debugging in this area of the code, your service uses a class to implement methods,  
  427. // you use SOAP RPC, and the client is .NET, please be aware of the following... 
  428. // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the 
  429. // method name. that is fine for naming the .NET methods. it is not fine for properly constructing 
  430. // the XML request and reading the XML response. you need to add the RequestElementName and 
  431. // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe 
  432. // generates for the method. these parameters are used to specify the correct XML element names 
  433. // for .NET to use, i.e. the names with the '.' in them. 
  434. // 
  435. $orig_methodname = $this->methodname; 
  436. if ($this->wsdl) { 
  437. if ($this->opData = $this->wsdl->getOperationData($this->methodname)) { 
  438. $this->debug('in invoke_method, found WSDL operation=' . $this->methodname); 
  439. $this->appendDebug('opData=' . $this->varDump($this->opData)); 
  440. } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) { 
  441. // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element 
  442. $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']); 
  443. $this->appendDebug('opData=' . $this->varDump($this->opData)); 
  444. $this->methodname = $this->opData['name']; 
  445. } else { 
  446. $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname); 
  447. $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service"); 
  448. return; 
  449. } else { 
  450. $this->debug('in invoke_method, no WSDL to validate method'); 
  451.  
  452. // if a . is present in $this->methodname, we see if there is a class in scope,  
  453. // which could be referred to. We will also distinguish between two deliminators,  
  454. // to allow methods to be called a the class or an instance 
  455. if (strpos($this->methodname, '..') > 0) { 
  456. $delim = '..'; 
  457. } else if (strpos($this->methodname, '.') > 0) { 
  458. $delim = '.'; 
  459. } else { 
  460. $delim = ''; 
  461. $this->debug("in invoke_method, delim=$delim"); 
  462.  
  463. $class = ''; 
  464. $method = ''; 
  465. if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) { 
  466. $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim)); 
  467. if (class_exists($try_class)) { 
  468. // get the class and method name 
  469. $class = $try_class; 
  470. $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim)); 
  471. $this->debug("in invoke_method, class=$class method=$method delim=$delim"); 
  472. } else { 
  473. $this->debug("in invoke_method, class=$try_class not found"); 
  474. } else { 
  475. $try_class = ''; 
  476. $this->debug("in invoke_method, no class to try"); 
  477.  
  478. // does method exist? 
  479. if ($class == '') { 
  480. if (!function_exists($this->methodname)) { 
  481. $this->debug("in invoke_method, function '$this->methodname' not found!"); 
  482. $this->result = 'fault: method not found'; 
  483. $this->fault('SOAP-ENV:Client', "method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')"); 
  484. return; 
  485. } else { 
  486. $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method; 
  487. if (!in_array($method_to_compare, get_class_methods($class))) { 
  488. $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!"); 
  489. $this->result = 'fault: method not found'; 
  490. $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')"); 
  491. return; 
  492.  
  493. // evaluate message, getting back parameters 
  494. // verify that request parameters match the method's signature 
  495. if(! $this->verify_method($this->methodname, $this->methodparams)) { 
  496. // debug 
  497. $this->debug('ERROR: request not verified against method signature'); 
  498. $this->result = 'fault: request failed validation against method signature'; 
  499. // return fault 
  500. $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service."); 
  501. return; 
  502.  
  503. // if there are parameters to pass 
  504. $this->debug('in invoke_method, params:'); 
  505. $this->appendDebug($this->varDump($this->methodparams)); 
  506. $this->debug("in invoke_method, calling '$this->methodname'"); 
  507. if (!function_exists('call_user_func_array')) { 
  508. if ($class == '') { 
  509. $this->debug('in invoke_method, calling function using eval()'); 
  510. $funcCall = "\$this->methodreturn = $this->methodname("; 
  511. } else { 
  512. if ($delim == '..') { 
  513. $this->debug('in invoke_method, calling class method using eval()'); 
  514. $funcCall = "\$this->methodreturn = ".$class."::".$method."("; 
  515. } else { 
  516. $this->debug('in invoke_method, calling instance method using eval()'); 
  517. // generate unique instance name 
  518. $instname = "\$inst_".time(); 
  519. $funcCall = $instname." = new ".$class."(); "; 
  520. $funcCall .= "\$this->methodreturn = ".$instname."->".$method."("; 
  521. if ($this->methodparams) { 
  522. foreach ($this->methodparams as $param) { 
  523. if (is_array($param) || is_object($param)) { 
  524. $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available'); 
  525. return; 
  526. $funcCall .= "\"$param\", "; 
  527. $funcCall = substr($funcCall, 0, -1); 
  528. $funcCall .= ');'; 
  529. $this->debug('in invoke_method, function call: '.$funcCall); 
  530. @eval($funcCall); 
  531. } else { 
  532. if ($class == '') { 
  533. $this->debug('in invoke_method, calling function using call_user_func_array()'); 
  534. $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array() 
  535. } elseif ($delim == '..') { 
  536. $this->debug('in invoke_method, calling class method using call_user_func_array()'); 
  537. $call_arg = array ($class, $method); 
  538. } else { 
  539. $this->debug('in invoke_method, calling instance method using call_user_func_array()'); 
  540. $instance = new $class (); 
  541. $call_arg = array(&$instance, $method); 
  542. if (is_array($this->methodparams)) { 
  543. $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams)); 
  544. } else { 
  545. $this->methodreturn = call_user_func_array($call_arg, array()); 
  546. $this->debug('in invoke_method, methodreturn:'); 
  547. $this->appendDebug($this->varDump($this->methodreturn)); 
  548. $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn)); 
  549.  
  550. /** 
  551. * serializes the return value from a PHP function into a full SOAP Envelope 
  552. * The following fields are set by this function (when successful) 
  553. * responseSOAP 
  554. * This sets the fault field on error 
  555. * @access private 
  556. */ 
  557. function serialize_return() { 
  558. $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI); 
  559. // if fault 
  560. if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) { 
  561. $this->debug('got a fault object from method'); 
  562. $this->fault = $this->methodreturn; 
  563. return; 
  564. } elseif ($this->methodreturnisliteralxml) { 
  565. $return_val = $this->methodreturn; 
  566. // returned value(s) 
  567. } else { 
  568. $this->debug('got a(n) '.gettype($this->methodreturn).' from method'); 
  569. $this->debug('serializing return value'); 
  570. if($this->wsdl) { 
  571. if (sizeof($this->opData['output']['parts']) > 1) { 
  572. $this->debug('more than one output part, so use the method return unchanged'); 
  573. $opParams = $this->methodreturn; 
  574. } elseif (sizeof($this->opData['output']['parts']) == 1) { 
  575. $this->debug('exactly one output part, so wrap the method return in a simple array'); 
  576. // TODO: verify that it is not already wrapped! 
  577. //foreach ($this->opData['output']['parts'] as $name => $type) { 
  578. // $this->debug('wrap in element named ' . $name); 
  579. //} 
  580. $opParams = array($this->methodreturn); 
  581. $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams); 
  582. $this->appendDebug($this->wsdl->getDebug()); 
  583. $this->wsdl->clearDebug(); 
  584. if($errstr = $this->wsdl->getError()) { 
  585. $this->debug('got wsdl error: '.$errstr); 
  586. $this->fault('SOAP-ENV:Server', 'unable to serialize result'); 
  587. return; 
  588. } else { 
  589. if (isset($this->methodreturn)) { 
  590. $return_val = $this->serialize_val($this->methodreturn, 'return'); 
  591. } else { 
  592. $return_val = ''; 
  593. $this->debug('in absence of WSDL, assume void return for backward compatibility'); 
  594. $this->debug('return value:'); 
  595. $this->appendDebug($this->varDump($return_val)); 
  596.  
  597. $this->debug('serializing response'); 
  598. if ($this->wsdl) { 
  599. $this->debug('have WSDL for serialization: style is ' . $this->opData['style']); 
  600. if ($this->opData['style'] == 'rpc') { 
  601. $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']); 
  602. if ($this->opData['output']['use'] == 'literal') { 
  603. // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace 
  604. if ($this->methodURI) { 
  605. $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 
  606. } else { 
  607. $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>'; 
  608. } else { 
  609. if ($this->methodURI) { 
  610. $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 
  611. } else { 
  612. $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>'; 
  613. } else { 
  614. $this->debug('style is not rpc for serialization: assume document'); 
  615. $payload = $return_val; 
  616. } else { 
  617. $this->debug('do not have WSDL for serialization: assume rpc/encoded'); 
  618. $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 
  619. $this->result = 'successful'; 
  620. if($this->wsdl) { 
  621. //if($this->debug_flag) { 
  622. $this->appendDebug($this->wsdl->getDebug()); 
  623. // } 
  624. if (isset($this->opData['output']['encodingStyle'])) { 
  625. $encodingStyle = $this->opData['output']['encodingStyle']; 
  626. } else { 
  627. $encodingStyle = ''; 
  628. // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. 
  629. $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle); 
  630. } else { 
  631. $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders); 
  632. $this->debug("Leaving serialize_return"); 
  633.  
  634. /** 
  635. * sends an HTTP response 
  636. * The following fields are set by this function (when successful) 
  637. * outgoing_headers 
  638. * response 
  639. * @access private 
  640. */ 
  641. function send_response() { 
  642. $this->debug('Enter send_response'); 
  643. if ($this->fault) { 
  644. $payload = $this->fault->serialize(); 
  645. $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error"; 
  646. $this->outgoing_headers[] = "Status: 500 Internal Server Error"; 
  647. } else { 
  648. $payload = $this->responseSOAP; 
  649. // Some combinations of PHP+Web server allow the Status 
  650. // to come through as a header. Since OK is the default 
  651. // just do nothing. 
  652. // $this->outgoing_headers[] = "HTTP/1.0 200 OK"; 
  653. // $this->outgoing_headers[] = "Status: 200 OK"; 
  654. // add debug data if in debug mode 
  655. if(isset($this->debug_flag) && $this->debug_flag) { 
  656. $payload .= $this->getDebugAsXMLComment(); 
  657. $this->outgoing_headers[] = "Server: $this->title Server v$this->version"; 
  658. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); 
  659. $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")"; 
  660. // Let the Web server decide about this 
  661. //$this->outgoing_headers[] = "Connection: Close\r\n"; 
  662. $payload = $this->getHTTPBody($payload); 
  663. $type = $this->getHTTPContentType(); 
  664. $charset = $this->getHTTPContentTypeCharset(); 
  665. $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : ''); 
  666. //begin code to compress payload - by John 
  667. // NOTE: there is no way to know whether the Web server will also compress 
  668. // this data. 
  669. if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {  
  670. if (strstr($this->headers['accept-encoding'], 'gzip')) { 
  671. if (function_exists('gzencode')) { 
  672. if (isset($this->debug_flag) && $this->debug_flag) { 
  673. $payload .= "<!-- Content being gzipped -->"; 
  674. $this->outgoing_headers[] = "Content-Encoding: gzip"; 
  675. $payload = gzencode($payload); 
  676. } else { 
  677. if (isset($this->debug_flag) && $this->debug_flag) { 
  678. $payload .= "<!-- Content will not be gzipped: no gzencode -->"; 
  679. } elseif (strstr($this->headers['accept-encoding'], 'deflate')) { 
  680. // Note: MSIE requires gzdeflate output (no Zlib header and checksum),  
  681. // instead of gzcompress output,  
  682. // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5) 
  683. if (function_exists('gzdeflate')) { 
  684. if (isset($this->debug_flag) && $this->debug_flag) { 
  685. $payload .= "<!-- Content being deflated -->"; 
  686. $this->outgoing_headers[] = "Content-Encoding: deflate"; 
  687. $payload = gzdeflate($payload); 
  688. } else { 
  689. if (isset($this->debug_flag) && $this->debug_flag) { 
  690. $payload .= "<!-- Content will not be deflated: no gzcompress -->"; 
  691. //end code 
  692. $this->outgoing_headers[] = "Content-Length: ".strlen($payload); 
  693. reset($this->outgoing_headers); 
  694. foreach($this->outgoing_headers as $hdr) { 
  695. header($hdr, false); 
  696. print $payload; 
  697. $this->response = join("\r\n", $this->outgoing_headers)."\r\n\r\n".$payload; 
  698.  
  699. /** 
  700. * takes the value that was created by parsing the request 
  701. * and compares to the method's signature, if available. 
  702. * @param string $operation The operation to be invoked 
  703. * @param array $request The array of parameter values 
  704. * @return boolean Whether the operation was found 
  705. * @access private 
  706. */ 
  707. function verify_method($operation, $request) { 
  708. if(isset($this->wsdl) && is_object($this->wsdl)) { 
  709. if($this->wsdl->getOperationData($operation)) { 
  710. return true; 
  711. } elseif(isset($this->operations[$operation])) { 
  712. return true; 
  713. return false; 
  714.  
  715. /** 
  716. * processes SOAP message received from client 
  717. * @param array $headers The HTTP headers 
  718. * @param string $data unprocessed request data from client 
  719. * @return mixed value of the message, decoded into a PHP type 
  720. * @access private 
  721. */ 
  722. function parseRequest($headers, $data) { 
  723. $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:'); 
  724. $this->appendDebug($this->varDump($headers)); 
  725. if (!isset($headers['content-type'])) { 
  726. $this->setError('Request not of type text/xml (no content-type header)'); 
  727. return false; 
  728. if (!strstr($headers['content-type'], 'text/xml')) { 
  729. $this->setError('Request not of type text/xml'); 
  730. return false; 
  731. if (strpos($headers['content-type'], '=')) { 
  732. $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); 
  733. $this->debug('Got response encoding: ' . $enc); 
  734. if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  735. $this->xml_encoding = strtoupper($enc); 
  736. } else { 
  737. $this->xml_encoding = 'US-ASCII'; 
  738. } else { 
  739. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  740. $this->xml_encoding = 'ISO-8859-1'; 
  741. $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser'); 
  742. // parse response, get soap parser obj 
  743. $parser = new nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8); 
  744. // parser debug 
  745. $this->debug("parser debug: \n".$parser->getDebug()); 
  746. // if fault occurred during message parsing 
  747. if($err = $parser->getError()) { 
  748. $this->result = 'fault: error in msg parsing: '.$err; 
  749. $this->fault('SOAP-ENV:Client', "error in msg parsing:\n".$err); 
  750. // else successfully parsed request into soapval object 
  751. } else { 
  752. // get/set methodname 
  753. $this->methodURI = $parser->root_struct_namespace; 
  754. $this->methodname = $parser->root_struct_name; 
  755. $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI); 
  756. $this->debug('calling parser->get_soapbody()'); 
  757. $this->methodparams = $parser->get_soapbody(); 
  758. // get SOAP headers 
  759. $this->requestHeaders = $parser->getHeaders(); 
  760. // get SOAP Header 
  761. $this->requestHeader = $parser->get_soapheader(); 
  762. // add document for doclit support 
  763. $this->document = $parser->document; 
  764.  
  765. /** 
  766. * gets the HTTP body for the current response. 
  767. * @param string $soapmsg The SOAP payload 
  768. * @return string The HTTP body, which includes the SOAP payload 
  769. * @access private 
  770. */ 
  771. function getHTTPBody($soapmsg) { 
  772. return $soapmsg; 
  773.  
  774. /** 
  775. * gets the HTTP content type for the current response. 
  776. * Note: getHTTPBody must be called before this. 
  777. * @return string the HTTP content type for the current response. 
  778. * @access private 
  779. */ 
  780. function getHTTPContentType() { 
  781. return 'text/xml'; 
  782.  
  783. /** 
  784. * gets the HTTP content type charset for the current response. 
  785. * returns false for non-text content types. 
  786. * Note: getHTTPBody must be called before this. 
  787. * @return string the HTTP content type charset for the current response. 
  788. * @access private 
  789. */ 
  790. function getHTTPContentTypeCharset() { 
  791. return $this->soap_defencoding; 
  792.  
  793. /** 
  794. * add a method to the dispatch map (this has been replaced by the register method) 
  795. * @param string $methodname 
  796. * @param string $in array of input values 
  797. * @param string $out array of output values 
  798. * @access public 
  799. * @deprecated 
  800. */ 
  801. function add_to_map($methodname, $in, $out) { 
  802. $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out); 
  803.  
  804. /** 
  805. * register a service function with the server 
  806. * @param string $name the name of the PHP function, class.method or class..method 
  807. * @param array $in assoc array of input values: key = param name, value = param type 
  808. * @param array $out assoc array of output values: key = param name, value = param type 
  809. * @param mixed $namespace the element namespace for the method or false 
  810. * @param mixed $soapaction the soapaction for the method or false 
  811. * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically 
  812. * @param mixed $use optional (encoded|literal) or false 
  813. * @param string $documentation optional Description to include in WSDL 
  814. * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) 
  815. * @access public 
  816. */ 
  817. function register($name, $in=array(), $out=array(), $namespace=false, $soapaction=false, $style=false, $use=false, $documentation='', $encodingStyle='') { 
  818. global $HTTP_SERVER_VARS; 
  819.  
  820. if($this->externalWSDLURL) { 
  821. die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.'); 
  822. if (! $name) { 
  823. die('You must specify a name when you register an operation'); 
  824. if (!is_array($in)) { 
  825. die('You must provide an array for operation inputs'); 
  826. if (!is_array($out)) { 
  827. die('You must provide an array for operation outputs'); 
  828. if(false == $namespace) { 
  829. if(false == $soapaction) { 
  830. if (isset($_SERVER)) { 
  831. $SERVER_NAME = $_SERVER['SERVER_NAME']; 
  832. $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; 
  833. $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); 
  834. } elseif (isset($HTTP_SERVER_VARS)) { 
  835. $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; 
  836. $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; 
  837. $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; 
  838. } else { 
  839. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); 
  840. if ($HTTPS == '1' || $HTTPS == 'on') { 
  841. $SCHEME = 'https'; 
  842. } else { 
  843. $SCHEME = 'http'; 
  844. $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name"; 
  845. if(false == $style) { 
  846. $style = "rpc"; 
  847. if(false == $use) { 
  848. $use = "encoded"; 
  849. if ($use == 'encoded' && $encodingStyle == '') { 
  850. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; 
  851.  
  852. $this->operations[$name] = array( 
  853. 'name' => $name,  
  854. 'in' => $in,  
  855. 'out' => $out,  
  856. 'namespace' => $namespace,  
  857. 'soapaction' => $soapaction,  
  858. 'style' => $style); 
  859. if($this->wsdl) { 
  860. $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle); 
  861. return true; 
  862.  
  863. /** 
  864. * Specify a fault to be returned to the client. 
  865. * This also acts as a flag to the server that a fault has occured. 
  866. * @param string $faultcode 
  867. * @param string $faultstring 
  868. * @param string $faultactor 
  869. * @param string $faultdetail 
  870. * @access public 
  871. */ 
  872. function fault($faultcode, $faultstring, $faultactor='', $faultdetail='') { 
  873. if ($faultdetail == '' && $this->debug_flag) { 
  874. $faultdetail = $this->getDebug(); 
  875. $this->fault = new nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail); 
  876. $this->fault->soap_defencoding = $this->soap_defencoding; 
  877.  
  878. /** 
  879. * Sets up wsdl object. 
  880. * Acts as a flag to enable internal WSDL generation 
  881. * @param string $serviceName, name of the service 
  882. * @param mixed $namespace optional 'tns' service namespace or false 
  883. * @param mixed $endpoint optional URL of service endpoint or false 
  884. * @param string $style optional (rpc|document) WSDL style (also specified by operation) 
  885. * @param string $transport optional SOAP transport 
  886. * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false 
  887. */ 
  888. function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) 
  889. global $HTTP_SERVER_VARS; 
  890.  
  891. if (isset($_SERVER)) { 
  892. $SERVER_NAME = $_SERVER['SERVER_NAME']; 
  893. $SERVER_PORT = $_SERVER['SERVER_PORT']; 
  894. $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; 
  895. $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); 
  896. } elseif (isset($HTTP_SERVER_VARS)) { 
  897. $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; 
  898. $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT']; 
  899. $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; 
  900. $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; 
  901. } else { 
  902. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); 
  903. // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI) 
  904. $colon = strpos($SERVER_NAME, ":"); 
  905. if ($colon) { 
  906. $SERVER_NAME = substr($SERVER_NAME, 0, $colon); 
  907. if ($SERVER_PORT == 80) { 
  908. $SERVER_PORT = ''; 
  909. } else { 
  910. $SERVER_PORT = ':' . $SERVER_PORT; 
  911. if(false == $namespace) { 
  912. $namespace = "http://$SERVER_NAME/soap/$serviceName"; 
  913.  
  914. if(false == $endpoint) { 
  915. if ($HTTPS == '1' || $HTTPS == 'on') { 
  916. $SCHEME = 'https'; 
  917. } else { 
  918. $SCHEME = 'http'; 
  919. $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME"; 
  920.  
  921. if(false == $schemaTargetNamespace) { 
  922. $schemaTargetNamespace = $namespace; 
  923.  
  924. $this->wsdl = new wsdl; 
  925. $this->wsdl->serviceName = $serviceName; 
  926. $this->wsdl->endpoint = $endpoint; 
  927. $this->wsdl->namespaces['tns'] = $namespace; 
  928. $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/'; 
  929. $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/'; 
  930. if ($schemaTargetNamespace != $namespace) { 
  931. $this->wsdl->namespaces['types'] = $schemaTargetNamespace; 
  932. $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces); 
  933. if ($style == 'document') { 
  934. $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified'; 
  935. $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace; 
  936. $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true); 
  937. $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true); 
  938. $this->wsdl->bindings[$serviceName.'Binding'] = array( 
  939. 'name'=>$serviceName.'Binding',  
  940. 'style'=>$style,  
  941. 'transport'=>$transport,  
  942. 'portType'=>$serviceName.'PortType'); 
  943. $this->wsdl->ports[$serviceName.'Port'] = array( 
  944. 'binding'=>$serviceName.'Binding',  
  945. 'location'=>$endpoint,  
  946. 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/'); 
/lib/nusoap.php  
  1. class nusoap_server extends nusoap_base { 
  2. /** 
  3. * HTTP headers of request 
  4. * @var array 
  5. * @access private 
  6. */ 
  7. var $headers = array(); 
  8. /** 
  9. * HTTP request 
  10. * @var string 
  11. * @access private 
  12. */ 
  13. var $request = ''; 
  14. /** 
  15. * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text) 
  16. * @var string 
  17. * @access public 
  18. */ 
  19. var $requestHeaders = ''; 
  20. /** 
  21. * SOAP Headers from request (parsed) 
  22. * @var mixed 
  23. * @access public 
  24. */ 
  25. var $requestHeader = NULL; 
  26. /** 
  27. * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text) 
  28. * @var string 
  29. * @access public 
  30. */ 
  31. var $document = ''; 
  32. /** 
  33. * SOAP payload for request (text) 
  34. * @var string 
  35. * @access public 
  36. */ 
  37. var $requestSOAP = ''; 
  38. /** 
  39. * requested method namespace URI 
  40. * @var string 
  41. * @access private 
  42. */ 
  43. var $methodURI = ''; 
  44. /** 
  45. * name of method requested 
  46. * @var string 
  47. * @access private 
  48. */ 
  49. var $methodname = ''; 
  50. /** 
  51. * method parameters from request 
  52. * @var array 
  53. * @access private 
  54. */ 
  55. var $methodparams = array(); 
  56. /** 
  57. * SOAP Action from request 
  58. * @var string 
  59. * @access private 
  60. */ 
  61. var $SOAPAction = ''; 
  62. /** 
  63. * character set encoding of incoming (request) messages 
  64. * @var string 
  65. * @access public 
  66. */ 
  67. var $xml_encoding = ''; 
  68. /** 
  69. * toggles whether the parser decodes element content w/ utf8_decode() 
  70. * @var boolean 
  71. * @access public 
  72. */ 
  73. var $decode_utf8 = true; 
  74.  
  75. /** 
  76. * HTTP headers of response 
  77. * @var array 
  78. * @access public 
  79. */ 
  80. var $outgoing_headers = array(); 
  81. /** 
  82. * HTTP response 
  83. * @var string 
  84. * @access private 
  85. */ 
  86. var $response = ''; 
  87. /** 
  88. * SOAP headers for response (text or array of soapval or associative array) 
  89. * @var mixed 
  90. * @access public 
  91. */ 
  92. var $responseHeaders = ''; 
  93. /** 
  94. * SOAP payload for response (text) 
  95. * @var string 
  96. * @access private 
  97. */ 
  98. var $responseSOAP = ''; 
  99. /** 
  100. * method return value to place in response 
  101. * @var mixed 
  102. * @access private 
  103. */ 
  104. var $methodreturn = false; 
  105. /** 
  106. * whether $methodreturn is a string of literal XML 
  107. * @var boolean 
  108. * @access public 
  109. */ 
  110. var $methodreturnisliteralxml = false; 
  111. /** 
  112. * SOAP fault for response (or false) 
  113. * @var mixed 
  114. * @access private 
  115. */ 
  116. var $fault = false; 
  117. /** 
  118. * text indication of result (for debugging) 
  119. * @var string 
  120. * @access private 
  121. */ 
  122. var $result = 'successful'; 
  123.  
  124. /** 
  125. * assoc array of operations => opData; operations are added by the register() 
  126. * method or by parsing an external WSDL definition 
  127. * @var array 
  128. * @access private 
  129. */ 
  130. var $operations = array(); 
  131. /** 
  132. * wsdl instance (if one) 
  133. * @var mixed 
  134. * @access private 
  135. */ 
  136. var $wsdl = false; 
  137. /** 
  138. * URL for WSDL (if one) 
  139. * @var mixed 
  140. * @access private 
  141. */ 
  142. var $externalWSDLURL = false; 
  143. /** 
  144. * whether to append debug to response as XML comment 
  145. * @var boolean 
  146. * @access public 
  147. */ 
  148. var $debug_flag = false; 
  149.  
  150.  
  151. /** 
  152. * constructor 
  153. * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. 
  154. * @param mixed $wsdl file path or URL (string), or wsdl instance (object) 
  155. * @access public 
  156. */ 
  157. function nusoap_server($wsdl=false) { 
  158. parent::nusoap_base(); 
  159. // turn on debugging? 
  160. global $debug; 
  161. global $HTTP_SERVER_VARS; 
  162.  
  163. if (isset($_SERVER)) { 
  164. $this->debug("_SERVER is defined:"); 
  165. $this->appendDebug($this->varDump($_SERVER)); 
  166. } elseif (isset($HTTP_SERVER_VARS)) { 
  167. $this->debug("HTTP_SERVER_VARS is defined:"); 
  168. $this->appendDebug($this->varDump($HTTP_SERVER_VARS)); 
  169. } else { 
  170. $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined."); 
  171.  
  172. if (isset($debug)) { 
  173. $this->debug("In nusoap_server, set debug_flag=$debug based on global flag"); 
  174. $this->debug_flag = $debug; 
  175. } elseif (isset($_SERVER['QUERY_STRING'])) { 
  176. $qs = explode('&', $_SERVER['QUERY_STRING']); 
  177. foreach ($qs as $v) { 
  178. if (substr($v, 0, 6) == 'debug=') { 
  179. $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1"); 
  180. $this->debug_flag = substr($v, 6); 
  181. } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { 
  182. $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']); 
  183. foreach ($qs as $v) { 
  184. if (substr($v, 0, 6) == 'debug=') { 
  185. $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2"); 
  186. $this->debug_flag = substr($v, 6); 
  187.  
  188. // wsdl 
  189. if($wsdl) { 
  190. $this->debug("In nusoap_server, WSDL is specified"); 
  191. if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) { 
  192. $this->wsdl = $wsdl; 
  193. $this->externalWSDLURL = $this->wsdl->wsdl; 
  194. $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL); 
  195. } else { 
  196. $this->debug('Create wsdl from ' . $wsdl); 
  197. $this->wsdl = new wsdl($wsdl); 
  198. $this->externalWSDLURL = $wsdl; 
  199. $this->appendDebug($this->wsdl->getDebug()); 
  200. $this->wsdl->clearDebug(); 
  201. if($err = $this->wsdl->getError()) { 
  202. die('WSDL ERROR: '.$err); 
  203.  
  204. /** 
  205. * processes request and returns response 
  206. * @param string $data usually is the value of $HTTP_RAW_POST_DATA 
  207. * @access public 
  208. */ 
  209. function service($data) { 
  210. global $HTTP_SERVER_VARS; 
  211.  
  212. if (isset($_SERVER['REQUEST_METHOD'])) { 
  213. $rm = $_SERVER['REQUEST_METHOD']; 
  214. } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) { 
  215. $rm = $HTTP_SERVER_VARS['REQUEST_METHOD']; 
  216. } else { 
  217. $rm = ''; 
  218.  
  219. if (isset($_SERVER['QUERY_STRING'])) { 
  220. $qs = $_SERVER['QUERY_STRING']; 
  221. } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { 
  222. $qs = $HTTP_SERVER_VARS['QUERY_STRING']; 
  223. } else { 
  224. $qs = ''; 
  225. $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data)); 
  226.  
  227. if ($rm == 'POST') { 
  228. $this->debug("In service, invoke the request"); 
  229. $this->parse_request($data); 
  230. if (! $this->fault) { 
  231. $this->invoke_method(); 
  232. if (! $this->fault) { 
  233. $this->serialize_return(); 
  234. $this->send_response(); 
  235. } elseif (preg_match('/wsdl/', $qs) ) { 
  236. $this->debug("In service, this is a request for WSDL"); 
  237. if ($this->externalWSDLURL) { 
  238. if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL 
  239. $this->debug("In service, re-direct for WSDL"); 
  240. header('Location: '.$this->externalWSDLURL); 
  241. } else { // assume file 
  242. $this->debug("In service, use file passthru for WSDL"); 
  243. header("Content-Type: text/xml\r\n"); 
  244. $pos = strpos($this->externalWSDLURL, "file://"); 
  245. if ($pos === false) { 
  246. $filename = $this->externalWSDLURL; 
  247. } else { 
  248. $filename = substr($this->externalWSDLURL, $pos + 7); 
  249. $fp = fopen($this->externalWSDLURL, 'r'); 
  250. fpassthru($fp); 
  251. } elseif ($this->wsdl) { 
  252. $this->debug("In service, serialize WSDL"); 
  253. header("Content-Type: text/xml; charset=ISO-8859-1\r\n"); 
  254. print $this->wsdl->serialize($this->debug_flag); 
  255. if ($this->debug_flag) { 
  256. $this->debug('wsdl:'); 
  257. $this->appendDebug($this->varDump($this->wsdl)); 
  258. print $this->getDebugAsXMLComment(); 
  259. } else { 
  260. $this->debug("In service, there is no WSDL"); 
  261. header("Content-Type: text/html; charset=ISO-8859-1\r\n"); 
  262. print "This service does not provide WSDL"; 
  263. } elseif ($this->wsdl) { 
  264. $this->debug("In service, return Web description"); 
  265. print $this->wsdl->webDescription(); 
  266. } else { 
  267. $this->debug("In service, no Web description"); 
  268. header("Content-Type: text/html; charset=ISO-8859-1\r\n"); 
  269. print "This service does not provide a Web description"; 
  270.  
  271. /** 
  272. * parses HTTP request headers. 
  273. * The following fields are set by this function (when successful) 
  274. * headers 
  275. * request 
  276. * xml_encoding 
  277. * SOAPAction 
  278. * @access private 
  279. */ 
  280. function parse_http_headers() { 
  281. global $HTTP_SERVER_VARS; 
  282.  
  283. $this->request = ''; 
  284. $this->SOAPAction = ''; 
  285. if(function_exists('getallheaders')) { 
  286. $this->debug("In parse_http_headers, use getallheaders"); 
  287. $headers = getallheaders(); 
  288. foreach($headers as $k=>$v) { 
  289. $k = strtolower($k); 
  290. $this->headers[$k] = $v; 
  291. $this->request .= "$k: $v\r\n"; 
  292. $this->debug("$k: $v"); 
  293. // get SOAPAction header 
  294. if(isset($this->headers['soapaction'])) { 
  295. $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']); 
  296. // get the character encoding of the incoming request 
  297. if(isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) { 
  298. $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1)); 
  299. if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  300. $this->xml_encoding = strtoupper($enc); 
  301. } else { 
  302. $this->xml_encoding = 'US-ASCII'; 
  303. } else { 
  304. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  305. $this->xml_encoding = 'ISO-8859-1'; 
  306. } elseif(isset($_SERVER) && is_array($_SERVER)) { 
  307. $this->debug("In parse_http_headers, use _SERVER"); 
  308. foreach ($_SERVER as $k => $v) { 
  309. if (substr($k, 0, 5) == 'HTTP_') { 
  310. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 
  311. } else { 
  312. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 
  313. if ($k == 'soapaction') { 
  314. // get SOAPAction header 
  315. $k = 'SOAPAction'; 
  316. $v = str_replace('"', '', $v); 
  317. $v = str_replace('\\', '', $v); 
  318. $this->SOAPAction = $v; 
  319. } else if ($k == 'content-type') { 
  320. // get the character encoding of the incoming request 
  321. if (strpos($v, '=')) { 
  322. $enc = substr(strstr($v, '='), 1); 
  323. $enc = str_replace('"', '', $enc); 
  324. $enc = str_replace('\\', '', $enc); 
  325. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  326. $this->xml_encoding = strtoupper($enc); 
  327. } else { 
  328. $this->xml_encoding = 'US-ASCII'; 
  329. } else { 
  330. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  331. $this->xml_encoding = 'ISO-8859-1'; 
  332. $this->headers[$k] = $v; 
  333. $this->request .= "$k: $v\r\n"; 
  334. $this->debug("$k: $v"); 
  335. } elseif (is_array($HTTP_SERVER_VARS)) { 
  336. $this->debug("In parse_http_headers, use HTTP_SERVER_VARS"); 
  337. foreach ($HTTP_SERVER_VARS as $k => $v) { 
  338. if (substr($k, 0, 5) == 'HTTP_') { 
  339. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5)); 
  340. } else { 
  341. $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k); 
  342. if ($k == 'soapaction') { 
  343. // get SOAPAction header 
  344. $k = 'SOAPAction'; 
  345. $v = str_replace('"', '', $v); 
  346. $v = str_replace('\\', '', $v); 
  347. $this->SOAPAction = $v; 
  348. } else if ($k == 'content-type') { 
  349. // get the character encoding of the incoming request 
  350. if (strpos($v, '=')) { 
  351. $enc = substr(strstr($v, '='), 1); 
  352. $enc = str_replace('"', '', $enc); 
  353. $enc = str_replace('\\', '', $enc); 
  354. if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  355. $this->xml_encoding = strtoupper($enc); 
  356. } else { 
  357. $this->xml_encoding = 'US-ASCII'; 
  358. } else { 
  359. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  360. $this->xml_encoding = 'ISO-8859-1'; 
  361. $this->headers[$k] = $v; 
  362. $this->request .= "$k: $v\r\n"; 
  363. $this->debug("$k: $v"); 
  364. } else { 
  365. $this->debug("In parse_http_headers, HTTP headers not accessible"); 
  366. $this->setError("HTTP headers not accessible"); 
  367.  
  368. /** 
  369. * parses a request 
  370. * The following fields are set by this function (when successful) 
  371. * headers 
  372. * request 
  373. * xml_encoding 
  374. * SOAPAction 
  375. * request 
  376. * requestSOAP 
  377. * methodURI 
  378. * methodname 
  379. * methodparams 
  380. * requestHeaders 
  381. * document 
  382. * This sets the fault field on error 
  383. * @param string $data XML string 
  384. * @access private 
  385. */ 
  386. function parse_request($data='') { 
  387. $this->debug('entering parse_request()'); 
  388. $this->parse_http_headers(); 
  389. $this->debug('got character encoding: '.$this->xml_encoding); 
  390. // uncompress if necessary 
  391. if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') { 
  392. $this->debug('got content encoding: ' . $this->headers['content-encoding']); 
  393. if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') { 
  394. // if decoding works, use it. else assume data wasn't gzencoded 
  395. if (function_exists('gzuncompress')) { 
  396. if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) { 
  397. $data = $degzdata; 
  398. } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) { 
  399. $data = $degzdata; 
  400. } else { 
  401. $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data'); 
  402. return; 
  403. } else { 
  404. $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data'); 
  405. return; 
  406. $this->request .= "\r\n".$data; 
  407. $data = $this->parseRequest($this->headers, $data); 
  408. $this->requestSOAP = $data; 
  409. $this->debug('leaving parse_request'); 
  410.  
  411. /** 
  412. * invokes a PHP function for the requested SOAP method 
  413. * The following fields are set by this function (when successful) 
  414. * methodreturn 
  415. * Note that the PHP function that is called may also set the following 
  416. * fields to affect the response sent to the client 
  417. * responseHeaders 
  418. * outgoing_headers 
  419. * This sets the fault field on error 
  420. * @access private 
  421. */ 
  422. function invoke_method() { 
  423. $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction); 
  424.  
  425. // 
  426. // if you are debugging in this area of the code, your service uses a class to implement methods,  
  427. // you use SOAP RPC, and the client is .NET, please be aware of the following... 
  428. // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the 
  429. // method name. that is fine for naming the .NET methods. it is not fine for properly constructing 
  430. // the XML request and reading the XML response. you need to add the RequestElementName and 
  431. // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe 
  432. // generates for the method. these parameters are used to specify the correct XML element names 
  433. // for .NET to use, i.e. the names with the '.' in them. 
  434. // 
  435. $orig_methodname = $this->methodname; 
  436. if ($this->wsdl) { 
  437. if ($this->opData = $this->wsdl->getOperationData($this->methodname)) { 
  438. $this->debug('in invoke_method, found WSDL operation=' . $this->methodname); 
  439. $this->appendDebug('opData=' . $this->varDump($this->opData)); 
  440. } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) { 
  441. // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element 
  442. $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']); 
  443. $this->appendDebug('opData=' . $this->varDump($this->opData)); 
  444. $this->methodname = $this->opData['name']; 
  445. } else { 
  446. $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname); 
  447. $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service"); 
  448. return; 
  449. } else { 
  450. $this->debug('in invoke_method, no WSDL to validate method'); 
  451.  
  452. // if a . is present in $this->methodname, we see if there is a class in scope,  
  453. // which could be referred to. We will also distinguish between two deliminators,  
  454. // to allow methods to be called a the class or an instance 
  455. if (strpos($this->methodname, '..') > 0) { 
  456. $delim = '..'; 
  457. } else if (strpos($this->methodname, '.') > 0) { 
  458. $delim = '.'; 
  459. } else { 
  460. $delim = ''; 
  461. $this->debug("in invoke_method, delim=$delim"); 
  462.  
  463. $class = ''; 
  464. $method = ''; 
  465. if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) { 
  466. $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim)); 
  467. if (class_exists($try_class)) { 
  468. // get the class and method name 
  469. $class = $try_class; 
  470. $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim)); 
  471. $this->debug("in invoke_method, class=$class method=$method delim=$delim"); 
  472. } else { 
  473. $this->debug("in invoke_method, class=$try_class not found"); 
  474. } else { 
  475. $try_class = ''; 
  476. $this->debug("in invoke_method, no class to try"); 
  477.  
  478. // does method exist? 
  479. if ($class == '') { 
  480. if (!function_exists($this->methodname)) { 
  481. $this->debug("in invoke_method, function '$this->methodname' not found!"); 
  482. $this->result = 'fault: method not found'; 
  483. $this->fault('SOAP-ENV:Client', "method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')"); 
  484. return; 
  485. } else { 
  486. $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method; 
  487. if (!in_array($method_to_compare, get_class_methods($class))) { 
  488. $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!"); 
  489. $this->result = 'fault: method not found'; 
  490. $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')"); 
  491. return; 
  492.  
  493. // evaluate message, getting back parameters 
  494. // verify that request parameters match the method's signature 
  495. if(! $this->verify_method($this->methodname, $this->methodparams)) { 
  496. // debug 
  497. $this->debug('ERROR: request not verified against method signature'); 
  498. $this->result = 'fault: request failed validation against method signature'; 
  499. // return fault 
  500. $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service."); 
  501. return; 
  502.  
  503. // if there are parameters to pass 
  504. $this->debug('in invoke_method, params:'); 
  505. $this->appendDebug($this->varDump($this->methodparams)); 
  506. $this->debug("in invoke_method, calling '$this->methodname'"); 
  507. if (!function_exists('call_user_func_array')) { 
  508. if ($class == '') { 
  509. $this->debug('in invoke_method, calling function using eval()'); 
  510. $funcCall = "\$this->methodreturn = $this->methodname("; 
  511. } else { 
  512. if ($delim == '..') { 
  513. $this->debug('in invoke_method, calling class method using eval()'); 
  514. $funcCall = "\$this->methodreturn = ".$class."::".$method."("; 
  515. } else { 
  516. $this->debug('in invoke_method, calling instance method using eval()'); 
  517. // generate unique instance name 
  518. $instname = "\$inst_".time(); 
  519. $funcCall = $instname." = new ".$class."(); "; 
  520. $funcCall .= "\$this->methodreturn = ".$instname."->".$method."("; 
  521. if ($this->methodparams) { 
  522. foreach ($this->methodparams as $param) { 
  523. if (is_array($param) || is_object($param)) { 
  524. $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available'); 
  525. return; 
  526. $funcCall .= "\"$param\", "; 
  527. $funcCall = substr($funcCall, 0, -1); 
  528. $funcCall .= ');'; 
  529. $this->debug('in invoke_method, function call: '.$funcCall); 
  530. @eval($funcCall); 
  531. } else { 
  532. if ($class == '') { 
  533. $this->debug('in invoke_method, calling function using call_user_func_array()'); 
  534. $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array() 
  535. } elseif ($delim == '..') { 
  536. $this->debug('in invoke_method, calling class method using call_user_func_array()'); 
  537. $call_arg = array ($class, $method); 
  538. } else { 
  539. $this->debug('in invoke_method, calling instance method using call_user_func_array()'); 
  540. $instance = new $class (); 
  541. $call_arg = array(&$instance, $method); 
  542. if (is_array($this->methodparams)) { 
  543. $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams)); 
  544. } else { 
  545. $this->methodreturn = call_user_func_array($call_arg, array()); 
  546. $this->debug('in invoke_method, methodreturn:'); 
  547. $this->appendDebug($this->varDump($this->methodreturn)); 
  548. $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn)); 
  549.  
  550. /** 
  551. * serializes the return value from a PHP function into a full SOAP Envelope 
  552. * The following fields are set by this function (when successful) 
  553. * responseSOAP 
  554. * This sets the fault field on error 
  555. * @access private 
  556. */ 
  557. function serialize_return() { 
  558. $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI); 
  559. // if fault 
  560. if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) { 
  561. $this->debug('got a fault object from method'); 
  562. $this->fault = $this->methodreturn; 
  563. return; 
  564. } elseif ($this->methodreturnisliteralxml) { 
  565. $return_val = $this->methodreturn; 
  566. // returned value(s) 
  567. } else { 
  568. $this->debug('got a(n) '.gettype($this->methodreturn).' from method'); 
  569. $this->debug('serializing return value'); 
  570. if($this->wsdl) { 
  571. if (sizeof($this->opData['output']['parts']) > 1) { 
  572. $this->debug('more than one output part, so use the method return unchanged'); 
  573. $opParams = $this->methodreturn; 
  574. } elseif (sizeof($this->opData['output']['parts']) == 1) { 
  575. $this->debug('exactly one output part, so wrap the method return in a simple array'); 
  576. // TODO: verify that it is not already wrapped! 
  577. //foreach ($this->opData['output']['parts'] as $name => $type) { 
  578. // $this->debug('wrap in element named ' . $name); 
  579. //} 
  580. $opParams = array($this->methodreturn); 
  581. $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams); 
  582. $this->appendDebug($this->wsdl->getDebug()); 
  583. $this->wsdl->clearDebug(); 
  584. if($errstr = $this->wsdl->getError()) { 
  585. $this->debug('got wsdl error: '.$errstr); 
  586. $this->fault('SOAP-ENV:Server', 'unable to serialize result'); 
  587. return; 
  588. } else { 
  589. if (isset($this->methodreturn)) { 
  590. $return_val = $this->serialize_val($this->methodreturn, 'return'); 
  591. } else { 
  592. $return_val = ''; 
  593. $this->debug('in absence of WSDL, assume void return for backward compatibility'); 
  594. $this->debug('return value:'); 
  595. $this->appendDebug($this->varDump($return_val)); 
  596.  
  597. $this->debug('serializing response'); 
  598. if ($this->wsdl) { 
  599. $this->debug('have WSDL for serialization: style is ' . $this->opData['style']); 
  600. if ($this->opData['style'] == 'rpc') { 
  601. $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']); 
  602. if ($this->opData['output']['use'] == 'literal') { 
  603. // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace 
  604. if ($this->methodURI) { 
  605. $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 
  606. } else { 
  607. $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>'; 
  608. } else { 
  609. if ($this->methodURI) { 
  610. $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 
  611. } else { 
  612. $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>'; 
  613. } else { 
  614. $this->debug('style is not rpc for serialization: assume document'); 
  615. $payload = $return_val; 
  616. } else { 
  617. $this->debug('do not have WSDL for serialization: assume rpc/encoded'); 
  618. $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 
  619. $this->result = 'successful'; 
  620. if($this->wsdl) { 
  621. //if($this->debug_flag) { 
  622. $this->appendDebug($this->wsdl->getDebug()); 
  623. // } 
  624. if (isset($this->opData['output']['encodingStyle'])) { 
  625. $encodingStyle = $this->opData['output']['encodingStyle']; 
  626. } else { 
  627. $encodingStyle = ''; 
  628. // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. 
  629. $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle); 
  630. } else { 
  631. $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders); 
  632. $this->debug("Leaving serialize_return"); 
  633.  
  634. /** 
  635. * sends an HTTP response 
  636. * The following fields are set by this function (when successful) 
  637. * outgoing_headers 
  638. * response 
  639. * @access private 
  640. */ 
  641. function send_response() { 
  642. $this->debug('Enter send_response'); 
  643. if ($this->fault) { 
  644. $payload = $this->fault->serialize(); 
  645. $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error"; 
  646. $this->outgoing_headers[] = "Status: 500 Internal Server Error"; 
  647. } else { 
  648. $payload = $this->responseSOAP; 
  649. // Some combinations of PHP+Web server allow the Status 
  650. // to come through as a header. Since OK is the default 
  651. // just do nothing. 
  652. // $this->outgoing_headers[] = "HTTP/1.0 200 OK"; 
  653. // $this->outgoing_headers[] = "Status: 200 OK"; 
  654. // add debug data if in debug mode 
  655. if(isset($this->debug_flag) && $this->debug_flag) { 
  656. $payload .= $this->getDebugAsXMLComment(); 
  657. $this->outgoing_headers[] = "Server: $this->title Server v$this->version"; 
  658. preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); 
  659. $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")"; 
  660. // Let the Web server decide about this 
  661. //$this->outgoing_headers[] = "Connection: Close\r\n"; 
  662. $payload = $this->getHTTPBody($payload); 
  663. $type = $this->getHTTPContentType(); 
  664. $charset = $this->getHTTPContentTypeCharset(); 
  665. $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : ''); 
  666. //begin code to compress payload - by John 
  667. // NOTE: there is no way to know whether the Web server will also compress 
  668. // this data. 
  669. if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {  
  670. if (strstr($this->headers['accept-encoding'], 'gzip')) { 
  671. if (function_exists('gzencode')) { 
  672. if (isset($this->debug_flag) && $this->debug_flag) { 
  673. $payload .= "<!-- Content being gzipped -->"; 
  674. $this->outgoing_headers[] = "Content-Encoding: gzip"; 
  675. $payload = gzencode($payload); 
  676. } else { 
  677. if (isset($this->debug_flag) && $this->debug_flag) { 
  678. $payload .= "<!-- Content will not be gzipped: no gzencode -->"; 
  679. } elseif (strstr($this->headers['accept-encoding'], 'deflate')) { 
  680. // Note: MSIE requires gzdeflate output (no Zlib header and checksum),  
  681. // instead of gzcompress output,  
  682. // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5) 
  683. if (function_exists('gzdeflate')) { 
  684. if (isset($this->debug_flag) && $this->debug_flag) { 
  685. $payload .= "<!-- Content being deflated -->"; 
  686. $this->outgoing_headers[] = "Content-Encoding: deflate"; 
  687. $payload = gzdeflate($payload); 
  688. } else { 
  689. if (isset($this->debug_flag) && $this->debug_flag) { 
  690. $payload .= "<!-- Content will not be deflated: no gzcompress -->"; 
  691. //end code 
  692. $this->outgoing_headers[] = "Content-Length: ".strlen($payload); 
  693. reset($this->outgoing_headers); 
  694. foreach($this->outgoing_headers as $hdr) { 
  695. header($hdr, false); 
  696. print $payload; 
  697. $this->response = join("\r\n", $this->outgoing_headers)."\r\n\r\n".$payload; 
  698.  
  699. /** 
  700. * takes the value that was created by parsing the request 
  701. * and compares to the method's signature, if available. 
  702. * @param string $operation The operation to be invoked 
  703. * @param array $request The array of parameter values 
  704. * @return boolean Whether the operation was found 
  705. * @access private 
  706. */ 
  707. function verify_method($operation, $request) { 
  708. if(isset($this->wsdl) && is_object($this->wsdl)) { 
  709. if($this->wsdl->getOperationData($operation)) { 
  710. return true; 
  711. } elseif(isset($this->operations[$operation])) { 
  712. return true; 
  713. return false; 
  714.  
  715. /** 
  716. * processes SOAP message received from client 
  717. * @param array $headers The HTTP headers 
  718. * @param string $data unprocessed request data from client 
  719. * @return mixed value of the message, decoded into a PHP type 
  720. * @access private 
  721. */ 
  722. function parseRequest($headers, $data) { 
  723. $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:'); 
  724. $this->appendDebug($this->varDump($headers)); 
  725. if (!isset($headers['content-type'])) { 
  726. $this->setError('Request not of type text/xml (no content-type header)'); 
  727. return false; 
  728. if (!strstr($headers['content-type'], 'text/xml')) { 
  729. $this->setError('Request not of type text/xml'); 
  730. return false; 
  731. if (strpos($headers['content-type'], '=')) { 
  732. $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); 
  733. $this->debug('Got response encoding: ' . $enc); 
  734. if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) { 
  735. $this->xml_encoding = strtoupper($enc); 
  736. } else { 
  737. $this->xml_encoding = 'US-ASCII'; 
  738. } else { 
  739. // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 
  740. $this->xml_encoding = 'ISO-8859-1'; 
  741. $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser'); 
  742. // parse response, get soap parser obj 
  743. $parser = new nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8); 
  744. // parser debug 
  745. $this->debug("parser debug: \n".$parser->getDebug()); 
  746. // if fault occurred during message parsing 
  747. if($err = $parser->getError()) { 
  748. $this->result = 'fault: error in msg parsing: '.$err; 
  749. $this->fault('SOAP-ENV:Client', "error in msg parsing:\n".$err); 
  750. // else successfully parsed request into soapval object 
  751. } else { 
  752. // get/set methodname 
  753. $this->methodURI = $parser->root_struct_namespace; 
  754. $this->methodname = $parser->root_struct_name; 
  755. $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI); 
  756. $this->debug('calling parser->get_soapbody()'); 
  757. $this->methodparams = $parser->get_soapbody(); 
  758. // get SOAP headers 
  759. $this->requestHeaders = $parser->getHeaders(); 
  760. // get SOAP Header 
  761. $this->requestHeader = $parser->get_soapheader(); 
  762. // add document for doclit support 
  763. $this->document = $parser->document; 
  764.  
  765. /** 
  766. * gets the HTTP body for the current response. 
  767. * @param string $soapmsg The SOAP payload 
  768. * @return string The HTTP body, which includes the SOAP payload 
  769. * @access private 
  770. */ 
  771. function getHTTPBody($soapmsg) { 
  772. return $soapmsg; 
  773.  
  774. /** 
  775. * gets the HTTP content type for the current response. 
  776. * Note: getHTTPBody must be called before this. 
  777. * @return string the HTTP content type for the current response. 
  778. * @access private 
  779. */ 
  780. function getHTTPContentType() { 
  781. return 'text/xml'; 
  782.  
  783. /** 
  784. * gets the HTTP content type charset for the current response. 
  785. * returns false for non-text content types. 
  786. * Note: getHTTPBody must be called before this. 
  787. * @return string the HTTP content type charset for the current response. 
  788. * @access private 
  789. */ 
  790. function getHTTPContentTypeCharset() { 
  791. return $this->soap_defencoding; 
  792.  
  793. /** 
  794. * add a method to the dispatch map (this has been replaced by the register method) 
  795. * @param string $methodname 
  796. * @param string $in array of input values 
  797. * @param string $out array of output values 
  798. * @access public 
  799. * @deprecated 
  800. */ 
  801. function add_to_map($methodname, $in, $out) { 
  802. $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out); 
  803.  
  804. /** 
  805. * register a service function with the server 
  806. * @param string $name the name of the PHP function, class.method or class..method 
  807. * @param array $in assoc array of input values: key = param name, value = param type 
  808. * @param array $out assoc array of output values: key = param name, value = param type 
  809. * @param mixed $namespace the element namespace for the method or false 
  810. * @param mixed $soapaction the soapaction for the method or false 
  811. * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically 
  812. * @param mixed $use optional (encoded|literal) or false 
  813. * @param string $documentation optional Description to include in WSDL 
  814. * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) 
  815. * @access public 
  816. */ 
  817. function register($name, $in=array(), $out=array(), $namespace=false, $soapaction=false, $style=false, $use=false, $documentation='', $encodingStyle='') { 
  818. global $HTTP_SERVER_VARS; 
  819.  
  820. if($this->externalWSDLURL) { 
  821. die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.'); 
  822. if (! $name) { 
  823. die('You must specify a name when you register an operation'); 
  824. if (!is_array($in)) { 
  825. die('You must provide an array for operation inputs'); 
  826. if (!is_array($out)) { 
  827. die('You must provide an array for operation outputs'); 
  828. if(false == $namespace) { 
  829. if(false == $soapaction) { 
  830. if (isset($_SERVER)) { 
  831. $SERVER_NAME = $_SERVER['SERVER_NAME']; 
  832. $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; 
  833. $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); 
  834. } elseif (isset($HTTP_SERVER_VARS)) { 
  835. $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; 
  836. $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; 
  837. $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; 
  838. } else { 
  839. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); 
  840. if ($HTTPS == '1' || $HTTPS == 'on') { 
  841. $SCHEME = 'https'; 
  842. } else { 
  843. $SCHEME = 'http'; 
  844. $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name"; 
  845. if(false == $style) { 
  846. $style = "rpc"; 
  847. if(false == $use) { 
  848. $use = "encoded"; 
  849. if ($use == 'encoded' && $encodingStyle == '') { 
  850. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; 
  851.  
  852. $this->operations[$name] = array( 
  853. 'name' => $name,  
  854. 'in' => $in,  
  855. 'out' => $out,  
  856. 'namespace' => $namespace,  
  857. 'soapaction' => $soapaction,  
  858. 'style' => $style); 
  859. if($this->wsdl) { 
  860. $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle); 
  861. return true; 
  862.  
  863. /** 
  864. * Specify a fault to be returned to the client. 
  865. * This also acts as a flag to the server that a fault has occured. 
  866. * @param string $faultcode 
  867. * @param string $faultstring 
  868. * @param string $faultactor 
  869. * @param string $faultdetail 
  870. * @access public 
  871. */ 
  872. function fault($faultcode, $faultstring, $faultactor='', $faultdetail='') { 
  873. if ($faultdetail == '' && $this->debug_flag) { 
  874. $faultdetail = $this->getDebug(); 
  875. $this->fault = new nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail); 
  876. $this->fault->soap_defencoding = $this->soap_defencoding; 
  877.  
  878. /** 
  879. * Sets up wsdl object. 
  880. * Acts as a flag to enable internal WSDL generation 
  881. * @param string $serviceName, name of the service 
  882. * @param mixed $namespace optional 'tns' service namespace or false 
  883. * @param mixed $endpoint optional URL of service endpoint or false 
  884. * @param string $style optional (rpc|document) WSDL style (also specified by operation) 
  885. * @param string $transport optional SOAP transport 
  886. * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false 
  887. */ 
  888. function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) 
  889. global $HTTP_SERVER_VARS; 
  890.  
  891. if (isset($_SERVER)) { 
  892. $SERVER_NAME = $_SERVER['SERVER_NAME']; 
  893. $SERVER_PORT = $_SERVER['SERVER_PORT']; 
  894. $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; 
  895. $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); 
  896. } elseif (isset($HTTP_SERVER_VARS)) { 
  897. $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; 
  898. $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT']; 
  899. $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; 
  900. $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; 
  901. } else { 
  902. $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); 
  903. // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI) 
  904. $colon = strpos($SERVER_NAME, ":"); 
  905. if ($colon) { 
  906. $SERVER_NAME = substr($SERVER_NAME, 0, $colon); 
  907. if ($SERVER_PORT == 80) { 
  908. $SERVER_PORT = ''; 
  909. } else { 
  910. $SERVER_PORT = ':' . $SERVER_PORT; 
  911. if(false == $namespace) { 
  912. $namespace = "http://$SERVER_NAME/soap/$serviceName"; 
  913.  
  914. if(false == $endpoint) { 
  915. if ($HTTPS == '1' || $HTTPS == 'on') { 
  916. $SCHEME = 'https'; 
  917. } else { 
  918. $SCHEME = 'http'; 
  919. $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME"; 
  920.  
  921. if(false == $schemaTargetNamespace) { 
  922. $schemaTargetNamespace = $namespace; 
  923.  
  924. $this->wsdl = new wsdl; 
  925. $this->wsdl->serviceName = $serviceName; 
  926. $this->wsdl->endpoint = $endpoint; 
  927. $this->wsdl->namespaces['tns'] = $namespace; 
  928. $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/'; 
  929. $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/'; 
  930. if ($schemaTargetNamespace != $namespace) { 
  931. $this->wsdl->namespaces['types'] = $schemaTargetNamespace; 
  932. $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces); 
  933. if ($style == 'document') { 
  934. $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified'; 
  935. $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace; 
  936. $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true); 
  937. $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true); 
  938. $this->wsdl->bindings[$serviceName.'Binding'] = array( 
  939. 'name'=>$serviceName.'Binding',  
  940. 'style'=>$style,  
  941. 'transport'=>$transport,  
  942. 'portType'=>$serviceName.'PortType'); 
  943. $this->wsdl->ports[$serviceName.'Port'] = array( 
  944. 'binding'=>$serviceName.'Binding',  
  945. 'location'=>$endpoint,  
  946. 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');