[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/nusoap/ -> class.soap_server.php (source)

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


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7