You are here

class soap_server in Salesforce Suite 5

Same name in this branch
  1. 5 includes/nusoap.php \soap_server
  2. 5 includes/nusoap.orig.php \soap_server
Same name and namespace in other branches
  1. 5.2 includes/nusoap.php \soap_server
  2. 5.2 includes/nusoap.orig.php \soap_server

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

NOTE: WSDL functionality is experimental

@author Dietrich Ayala <dietrich@ganx4.com> @access public

Hierarchy

Expanded class hierarchy of soap_server

File

includes/nusoap.php, line 3042

View source
class soap_server extends nusoap_base {

  /**
   * HTTP headers of request
   * @var array
   * @access private
   */
  var $headers = [];

  /**
   * HTTP request
   * @var string
   * @access private
   */
  var $request = '';

  /**
   * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
   * @var string
   * @access public
   */
  var $requestHeaders = '';

  /**
   * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
   * @var string
   * @access public
   */
  var $document = '';

  /**
   * SOAP payload for request (text)
   * @var string
   * @access public
   */
  var $requestSOAP = '';

  /**
   * requested method namespace URI
   * @var string
   * @access private
   */
  var $methodURI = '';

  /**
   * name of method requested
   * @var string
   * @access private
   */
  var $methodname = '';

  /**
   * method parameters from request
   * @var array
   * @access private
   */
  var $methodparams = [];

  /**
   * SOAP Action from request
   * @var string
   * @access private
   */
  var $SOAPAction = '';

  /**
   * character set encoding of incoming (request) messages
   * @var string
   * @access public
   */
  var $xml_encoding = '';

  /**
   * toggles whether the parser decodes element content w/ utf8_decode()
   * @var boolean
   * @access public
   */
  var $decode_utf8 = true;

  /**
   * HTTP headers of response
   * @var array
   * @access public
   */
  var $outgoing_headers = [];

  /**
   * HTTP response
   * @var string
   * @access private
   */
  var $response = '';

  /**
   * SOAP headers for response (text)
   * @var string
   * @access public
   */
  var $responseHeaders = '';

  /**
   * SOAP payload for response (text)
   * @var string
   * @access private
   */
  var $responseSOAP = '';

  /**
   * method return value to place in response
   * @var mixed
   * @access private
   */
  var $methodreturn = false;

  /**
   * whether $methodreturn is a string of literal XML
   * @var boolean
   * @access public
   */
  var $methodreturnisliteralxml = false;

  /**
   * SOAP fault for response (or false)
   * @var mixed
   * @access private
   */
  var $fault = false;

  /**
   * text indication of result (for debugging)
   * @var string
   * @access private
   */
  var $result = 'successful';

  /**
   * assoc array of operations => opData; operations are added by the register()
   * method or by parsing an external WSDL definition
   * @var array
   * @access private
   */
  var $operations = [];

  /**
   * wsdl instance (if one)
   * @var mixed
   * @access private
   */
  var $wsdl = false;

  /**
   * URL for WSDL (if one)
   * @var mixed
   * @access private
   */
  var $externalWSDLURL = false;

  /**
   * whether to append debug to response as XML comment
   * @var boolean
   * @access public
   */
  var $debug_flag = false;

  /**
   * constructor
   * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
   *
   * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
   * @access   public
   */
  function soap_server($wsdl = false) {
    parent::nusoap_base();

    // turn on debugging?
    global $debug;
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER)) {
      $this
        ->debug("_SERVER is defined:");
      $this
        ->appendDebug($this
        ->varDump($_SERVER));
    }
    elseif (isset($HTTP_SERVER_VARS)) {
      $this
        ->debug("HTTP_SERVER_VARS is defined:");
      $this
        ->appendDebug($this
        ->varDump($HTTP_SERVER_VARS));
    }
    else {
      $this
        ->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
    }
    if (isset($debug)) {
      $this
        ->debug("In soap_server, set debug_flag={$debug} based on global flag");
      $this->debug_flag = $debug;
    }
    elseif (isset($_SERVER['QUERY_STRING'])) {
      $qs = explode('&', $_SERVER['QUERY_STRING']);
      foreach ($qs as $v) {
        if (substr($v, 0, 6) == 'debug=') {
          $this
            ->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
          $this->debug_flag = substr($v, 6);
        }
      }
    }
    elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
      $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
      foreach ($qs as $v) {
        if (substr($v, 0, 6) == 'debug=') {
          $this
            ->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
          $this->debug_flag = substr($v, 6);
        }
      }
    }

    // wsdl
    if ($wsdl) {
      $this
        ->debug("In soap_server, WSDL is specified");
      if (is_object($wsdl) && get_class($wsdl) == 'wsdl') {
        $this->wsdl = $wsdl;
        $this->externalWSDLURL = $this->wsdl->wsdl;
        $this
          ->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
      }
      else {
        $this
          ->debug('Create wsdl from ' . $wsdl);
        $this->wsdl = new wsdl($wsdl);
        $this->externalWSDLURL = $wsdl;
      }
      $this
        ->appendDebug($this->wsdl
        ->getDebug());
      $this->wsdl
        ->clearDebug();
      if ($err = $this->wsdl
        ->getError()) {
        die('WSDL ERROR: ' . $err);
      }
    }
  }

  /**
   * processes request and returns response
   *
   * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
   * @access   public
   */
  function service($data) {
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER['QUERY_STRING'])) {
      $qs = $_SERVER['QUERY_STRING'];
    }
    elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
      $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
    }
    else {
      $qs = '';
    }
    $this
      ->debug("In service, query string={$qs}");
    if (ereg('wsdl', $qs)) {
      $this
        ->debug("In service, this is a request for WSDL");
      if ($this->externalWSDLURL) {
        if (strpos($this->externalWSDLURL, "://") !== false) {

          // assume URL
          header('Location: ' . $this->externalWSDLURL);
        }
        else {

          // assume file
          header("Content-Type: text/xml\r\n");
          $fp = fopen($this->externalWSDLURL, 'r');
          fpassthru($fp);
        }
      }
      elseif ($this->wsdl) {
        header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
        print $this->wsdl
          ->serialize($this->debug_flag);
        if ($this->debug_flag) {
          $this
            ->debug('wsdl:');
          $this
            ->appendDebug($this
            ->varDump($this->wsdl));
          print $this
            ->getDebugAsXMLComment();
        }
      }
      else {
        header("Content-Type: text/html; charset=ISO-8859-1\r\n");
        print "This service does not provide WSDL";
      }
    }
    elseif ($data == '' && $this->wsdl) {
      $this
        ->debug("In service, there is no data, so return Web description");
      print $this->wsdl
        ->webDescription();
    }
    else {
      $this
        ->debug("In service, invoke the request");
      $this
        ->parse_request($data);
      if (!$this->fault) {
        $this
          ->invoke_method();
      }
      if (!$this->fault) {
        $this
          ->serialize_return();
      }
      $this
        ->send_response();
    }
  }

  /**
   * parses HTTP request headers.
   *
   * The following fields are set by this function (when successful)
   *
   * headers
   * request
   * xml_encoding
   * SOAPAction
   *
   * @access   private
   */
  function parse_http_headers() {
    global $HTTP_SERVER_VARS;
    $this->request = '';
    $this->SOAPAction = '';
    if (function_exists('getallheaders')) {
      $this
        ->debug("In parse_http_headers, use getallheaders");
      $headers = getallheaders();
      foreach ($headers as $k => $v) {
        $k = strtolower($k);
        $this->headers[$k] = $v;
        $this->request .= "{$k}: {$v}\r\n";
        $this
          ->debug("{$k}: {$v}");
      }

      // get SOAPAction header
      if (isset($this->headers['soapaction'])) {
        $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
      }

      // get the character encoding of the incoming request
      if (isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) {
        $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1));
        if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
          $this->xml_encoding = strtoupper($enc);
        }
        else {
          $this->xml_encoding = 'US-ASCII';
        }
      }
      else {

        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
        $this->xml_encoding = 'ISO-8859-1';
      }
    }
    elseif (isset($_SERVER) && is_array($_SERVER)) {
      $this
        ->debug("In parse_http_headers, use _SERVER");
      foreach ($_SERVER as $k => $v) {
        if (substr($k, 0, 5) == 'HTTP_') {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
          $k = strtolower(substr($k, 5));
        }
        else {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
          $k = strtolower($k);
        }
        if ($k == 'soapaction') {

          // get SOAPAction header
          $k = 'SOAPAction';
          $v = str_replace('"', '', $v);
          $v = str_replace('\\', '', $v);
          $this->SOAPAction = $v;
        }
        else {
          if ($k == 'content-type') {

            // get the character encoding of the incoming request
            if (strpos($v, '=')) {
              $enc = substr(strstr($v, '='), 1);
              $enc = str_replace('"', '', $enc);
              $enc = str_replace('\\', '', $enc);
              if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
                $this->xml_encoding = strtoupper($enc);
              }
              else {
                $this->xml_encoding = 'US-ASCII';
              }
            }
            else {

              // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
              $this->xml_encoding = 'ISO-8859-1';
            }
          }
        }
        $this->headers[$k] = $v;
        $this->request .= "{$k}: {$v}\r\n";
        $this
          ->debug("{$k}: {$v}");
      }
    }
    elseif (is_array($HTTP_SERVER_VARS)) {
      $this
        ->debug("In parse_http_headers, use HTTP_SERVER_VARS");
      foreach ($HTTP_SERVER_VARS as $k => $v) {
        if (substr($k, 0, 5) == 'HTTP_') {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
          $k = strtolower(substr($k, 5));
        }
        else {
          $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
          $k = strtolower($k);
        }
        if ($k == 'soapaction') {

          // get SOAPAction header
          $k = 'SOAPAction';
          $v = str_replace('"', '', $v);
          $v = str_replace('\\', '', $v);
          $this->SOAPAction = $v;
        }
        else {
          if ($k == 'content-type') {

            // get the character encoding of the incoming request
            if (strpos($v, '=')) {
              $enc = substr(strstr($v, '='), 1);
              $enc = str_replace('"', '', $enc);
              $enc = str_replace('\\', '', $enc);
              if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
                $this->xml_encoding = strtoupper($enc);
              }
              else {
                $this->xml_encoding = 'US-ASCII';
              }
            }
            else {

              // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
              $this->xml_encoding = 'ISO-8859-1';
            }
          }
        }
        $this->headers[$k] = $v;
        $this->request .= "{$k}: {$v}\r\n";
        $this
          ->debug("{$k}: {$v}");
      }
    }
    else {
      $this
        ->debug("In parse_http_headers, HTTP headers not accessible");
      $this
        ->setError("HTTP headers not accessible");
    }
  }

  /**
   * parses a request
   *
   * The following fields are set by this function (when successful)
   *
   * headers
   * request
   * xml_encoding
   * SOAPAction
   * request
   * requestSOAP
   * methodURI
   * methodname
   * methodparams
   * requestHeaders
   * document
   *
   * This sets the fault field on error
   *
   * @param    string $data XML string
   * @access   private
   */
  function parse_request($data = '') {
    $this
      ->debug('entering parse_request()');
    $this
      ->parse_http_headers();
    $this
      ->debug('got character encoding: ' . $this->xml_encoding);

    // uncompress if necessary
    if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
      $this
        ->debug('got content encoding: ' . $this->headers['content-encoding']);
      if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {

        // if decoding works, use it. else assume data wasn't gzencoded
        if (function_exists('gzuncompress')) {
          if ($this->headers['content-encoding'] == 'deflate' && ($degzdata = @gzuncompress($data))) {
            $data = $degzdata;
          }
          elseif ($this->headers['content-encoding'] == 'gzip' && ($degzdata = gzinflate(substr($data, 10)))) {
            $data = $degzdata;
          }
          else {
            $this
              ->fault('Client', 'Errors occurred when trying to decode the data');
            return;
          }
        }
        else {
          $this
            ->fault('Client', 'This Server does not support compressed data');
          return;
        }
      }
    }
    $this->request .= "\r\n" . $data;
    $data = $this
      ->parseRequest($this->headers, $data);
    $this->requestSOAP = $data;
    $this
      ->debug('leaving parse_request');
  }

  /**
   * invokes a PHP function for the requested SOAP method
   *
   * The following fields are set by this function (when successful)
   *
   * methodreturn
   *
   * Note that the PHP function that is called may also set the following
   * fields to affect the response sent to the client
   *
   * responseHeaders
   * outgoing_headers
   *
   * This sets the fault field on error
   *
   * @access   private
   */
  function invoke_method() {
    $this
      ->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
    if ($this->wsdl) {
      if ($this->opData = $this->wsdl
        ->getOperationData($this->methodname)) {
        $this
          ->debug('in invoke_method, found WSDL operation=' . $this->methodname);
        $this
          ->appendDebug('opData=' . $this
          ->varDump($this->opData));
      }
      elseif ($this->opData = $this->wsdl
        ->getOperationDataForSoapAction($this->SOAPAction)) {

        // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
        $this
          ->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
        $this
          ->appendDebug('opData=' . $this
          ->varDump($this->opData));
        $this->methodname = $this->opData['name'];
      }
      else {
        $this
          ->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
        $this
          ->fault('Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
        return;
      }
    }
    else {
      $this
        ->debug('in invoke_method, no WSDL to validate method');
    }

    // if a . is present in $this->methodname, we see if there is a class in scope,
    // which could be referred to. We will also distinguish between two deliminators,
    // to allow methods to be called a the class or an instance
    $class = '';
    $method = '';
    if (strpos($this->methodname, '..') > 0) {
      $delim = '..';
    }
    else {
      if (strpos($this->methodname, '.') > 0) {
        $delim = '.';
      }
      else {
        $delim = '';
      }
    }
    if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 && class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {

      // get the class and method name
      $class = substr($this->methodname, 0, strpos($this->methodname, $delim));
      $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
      $this
        ->debug("in invoke_method, class={$class} method={$method} delim={$delim}");
    }

    // does method exist?
    if ($class == '') {
      if (!function_exists($this->methodname)) {
        $this
          ->debug("in invoke_method, function '{$this->methodname}' not found!");
        $this->result = 'fault: method not found';
        $this
          ->fault('Client', "method '{$this->methodname}' not defined in service");
        return;
      }
    }
    else {
      $method_to_compare = substr(phpversion(), 0, 2) == '4.' ? strtolower($method) : $method;
      if (!in_array($method_to_compare, get_class_methods($class))) {
        $this
          ->debug("in invoke_method, method '{$this->methodname}' not found in class '{$class}'!");
        $this->result = 'fault: method not found';
        $this
          ->fault('Client', "method '{$this->methodname}' not defined in service");
        return;
      }
    }

    // evaluate message, getting back parameters
    // verify that request parameters match the method's signature
    if (!$this
      ->verify_method($this->methodname, $this->methodparams)) {

      // debug
      $this
        ->debug('ERROR: request not verified against method signature');
      $this->result = 'fault: request failed validation against method signature';

      // return fault
      $this
        ->fault('Client', "Operation '{$this->methodname}' not defined in service.");
      return;
    }

    // if there are parameters to pass
    $this
      ->debug('in invoke_method, params:');
    $this
      ->appendDebug($this
      ->varDump($this->methodparams));
    $this
      ->debug("in invoke_method, calling '{$this->methodname}'");
    if (!function_exists('call_user_func_array')) {
      if ($class == '') {
        $this
          ->debug('in invoke_method, calling function using eval()');
        $funcCall = "\$this->methodreturn = {$this->methodname}(";
      }
      else {
        if ($delim == '..') {
          $this
            ->debug('in invoke_method, calling class method using eval()');
          $funcCall = "\$this->methodreturn = " . $class . "::" . $method . "(";
        }
        else {
          $this
            ->debug('in invoke_method, calling instance method using eval()');

          // generate unique instance name
          $instname = "\$inst_" . time();
          $funcCall = $instname . " = new " . $class . "(); ";
          $funcCall .= "\$this->methodreturn = " . $instname . "->" . $method . "(";
        }
      }
      if ($this->methodparams) {
        foreach ($this->methodparams as $param) {
          if (is_array($param)) {
            $this
              ->fault('Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
            return;
          }
          $funcCall .= "\"{$param}\",";
        }
        $funcCall = substr($funcCall, 0, -1);
      }
      $funcCall .= ');';
      $this
        ->debug('in invoke_method, function call: ' . $funcCall);
      @eval($funcCall);
    }
    else {
      if ($class == '') {
        $this
          ->debug('in invoke_method, calling function using call_user_func_array()');
        $call_arg = "{$this->methodname}";

        // straight assignment changes $this->methodname to lower case after call_user_func_array()
      }
      elseif ($delim == '..') {
        $this
          ->debug('in invoke_method, calling class method using call_user_func_array()');
        $call_arg = array(
          $class,
          $method,
        );
      }
      else {
        $this
          ->debug('in invoke_method, calling instance method using call_user_func_array()');
        $instance = new $class();
        $call_arg = array(
          &$instance,
          $method,
        );
      }
      $this->methodreturn = call_user_func_array($call_arg, $this->methodparams);
    }
    $this
      ->debug('in invoke_method, methodreturn:');
    $this
      ->appendDebug($this
      ->varDump($this->methodreturn));
    $this
      ->debug("in invoke_method, called method {$this->methodname}, received {$this->methodreturn} of type " . gettype($this->methodreturn));
  }

  /**
   * serializes the return value from a PHP function into a full SOAP Envelope
   *
   * The following fields are set by this function (when successful)
   *
   * responseSOAP
   *
   * This sets the fault field on error
   *
   * @access   private
   */
  function serialize_return() {
    $this
      ->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);

    // if fault
    if (isset($this->methodreturn) && get_class($this->methodreturn) == 'soap_fault') {
      $this
        ->debug('got a fault object from method');
      $this->fault = $this->methodreturn;
      return;
    }
    elseif ($this->methodreturnisliteralxml) {
      $return_val = $this->methodreturn;

      // returned value(s)
    }
    else {
      $this
        ->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
      $this
        ->debug('serializing return value');
      if ($this->wsdl) {

        // weak attempt at supporting multiple output params
        if (sizeof($this->opData['output']['parts']) > 1) {
          $opParams = $this->methodreturn;
        }
        else {

          // TODO: is this really necessary?
          $opParams = array(
            $this->methodreturn,
          );
        }
        $return_val = $this->wsdl
          ->serializeRPCParameters($this->methodname, 'output', $opParams);
        $this
          ->appendDebug($this->wsdl
          ->getDebug());
        $this->wsdl
          ->clearDebug();
        if ($errstr = $this->wsdl
          ->getError()) {
          $this
            ->debug('got wsdl error: ' . $errstr);
          $this
            ->fault('Server', 'unable to serialize result');
          return;
        }
      }
      else {
        if (isset($this->methodreturn)) {
          $return_val = $this
            ->serialize_val($this->methodreturn, 'return');
        }
        else {
          $return_val = '';
          $this
            ->debug('in absence of WSDL, assume void return for backward compatibility');
        }
      }
    }
    $this
      ->debug('return value:');
    $this
      ->appendDebug($this
      ->varDump($return_val));
    $this
      ->debug('serializing response');
    if ($this->wsdl) {
      $this
        ->debug('have WSDL for serialization: style is ' . $this->opData['style']);
      if ($this->opData['style'] == 'rpc') {
        $this
          ->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
        if ($this->opData['output']['use'] == 'literal') {
          $payload = '<' . $this->methodname . 'Response xmlns="' . $this->methodURI . '">' . $return_val . '</' . $this->methodname . "Response>";
        }
        else {
          $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
        }
      }
      else {
        $this
          ->debug('style is not rpc for serialization: assume document');
        $payload = $return_val;
      }
    }
    else {
      $this
        ->debug('do not have WSDL for serialization: assume rpc/encoded');
      $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
    }
    $this->result = 'successful';
    if ($this->wsdl) {

      //if($this->debug_flag){
      $this
        ->appendDebug($this->wsdl
        ->getDebug());

      //	}
      if (isset($opData['output']['encodingStyle'])) {
        $encodingStyle = $opData['output']['encodingStyle'];
      }
      else {
        $encodingStyle = '';
      }

      // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
      $this->responseSOAP = $this
        ->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $encodingStyle);
    }
    else {
      $this->responseSOAP = $this
        ->serializeEnvelope($payload, $this->responseHeaders);
    }
    $this
      ->debug("Leaving serialize_return");
  }

  /**
   * sends an HTTP response
   *
   * The following fields are set by this function (when successful)
   *
   * outgoing_headers
   * response
   *
   * @access   private
   */
  function send_response() {
    $this
      ->debug('Enter send_response');
    if ($this->fault) {
      $payload = $this->fault
        ->serialize();
      $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
      $this->outgoing_headers[] = "Status: 500 Internal Server Error";
    }
    else {
      $payload = $this->responseSOAP;

      // Some combinations of PHP+Web server allow the Status
      // to come through as a header.  Since OK is the default
      // just do nothing.
      // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
      // $this->outgoing_headers[] = "Status: 200 OK";
    }

    // add debug data if in debug mode
    if (isset($this->debug_flag) && $this->debug_flag) {
      $payload .= $this
        ->getDebugAsXMLComment();
    }
    $this->outgoing_headers[] = "Server: {$this->title} Server v{$this->version}";
    ereg('\\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
    $this->outgoing_headers[] = "X-SOAP-Server: {$this->title}/{$this->version} (" . $rev[1] . ")";

    // Let the Web server decide about this

    //$this->outgoing_headers[] = "Connection: Close\r\n";
    $payload = $this
      ->getHTTPBody($payload);
    $type = $this
      ->getHTTPContentType();
    $charset = $this
      ->getHTTPContentTypeCharset();
    $this->outgoing_headers[] = "Content-Type: {$type}" . ($charset ? '; charset=' . $charset : '');

    //begin code to compress payload - by John

    // NOTE: there is no way to know whether the Web server will also compress
    // this data.
    if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
      if (strstr($this->headers['accept-encoding'], 'gzip')) {
        if (function_exists('gzencode')) {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content being gzipped -->";
          }
          $this->outgoing_headers[] = "Content-Encoding: gzip";
          $payload = gzencode($payload);
        }
        else {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content will not be gzipped: no gzencode -->";
          }
        }
      }
      elseif (strstr($this->headers['accept-encoding'], 'deflate')) {

        // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
        // instead of gzcompress output,
        // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
        if (function_exists('gzdeflate')) {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content being deflated -->";
          }
          $this->outgoing_headers[] = "Content-Encoding: deflate";
          $payload = gzdeflate($payload);
        }
        else {
          if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= "<!-- Content will not be deflated: no gzcompress -->";
          }
        }
      }
    }

    //end code
    $this->outgoing_headers[] = "Content-Length: " . strlen($payload);
    reset($this->outgoing_headers);
    foreach ($this->outgoing_headers as $hdr) {
      header($hdr, false);
    }
    print $payload;
    $this->response = join("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
  }

  /**
   * takes the value that was created by parsing the request
   * and compares to the method's signature, if available.
   *
   * @param	string	$operation	The operation to be invoked
   * @param	array	$request	The array of parameter values
   * @return	boolean	Whether the operation was found
   * @access   private
   */
  function verify_method($operation, $request) {
    if (isset($this->wsdl) && is_object($this->wsdl)) {
      if ($this->wsdl
        ->getOperationData($operation)) {
        return true;
      }
    }
    elseif (isset($this->operations[$operation])) {
      return true;
    }
    return false;
  }

  /**
   * processes SOAP message received from client
   *
   * @param	array	$headers	The HTTP headers
   * @param	string	$data		unprocessed request data from client
   * @return	mixed	value of the message, decoded into a PHP type
   * @access   private
   */
  function parseRequest($headers, $data) {
    $this
      ->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
    if (!strstr($headers['content-type'], 'text/xml')) {
      $this
        ->setError('Request not of type text/xml');
      return false;
    }
    if (strpos($headers['content-type'], '=')) {
      $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
      $this
        ->debug('Got response encoding: ' . $enc);
      if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
        $this->xml_encoding = strtoupper($enc);
      }
      else {
        $this->xml_encoding = 'US-ASCII';
      }
    }
    else {

      // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
      $this->xml_encoding = 'ISO-8859-1';
    }
    $this
      ->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');

    // parse response, get soap parser obj
    $parser = new soap_parser($data, $this->xml_encoding, '', $this->decode_utf8);

    // parser debug
    $this
      ->debug("parser debug: \n" . $parser
      ->getDebug());

    // if fault occurred during message parsing
    if ($err = $parser
      ->getError()) {
      $this->result = 'fault: error in msg parsing: ' . $err;
      $this
        ->fault('Client', "error in msg parsing:\n" . $err);

      // else successfully parsed request into soapval object
    }
    else {

      // get/set methodname
      $this->methodURI = $parser->root_struct_namespace;
      $this->methodname = $parser->root_struct_name;
      $this
        ->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
      $this
        ->debug('calling parser->get_response()');
      $this->methodparams = $parser
        ->get_response();

      // get SOAP headers
      $this->requestHeaders = $parser
        ->getHeaders();

      // add document for doclit support
      $this->document = $parser->document;
    }
  }

  /**
   * gets the HTTP body for the current response.
   *
   * @param string $soapmsg The SOAP payload
   * @return string The HTTP body, which includes the SOAP payload
   * @access private
   */
  function getHTTPBody($soapmsg) {
    return $soapmsg;
  }

  /**
   * gets the HTTP content type for the current response.
   *
   * Note: getHTTPBody must be called before this.
   *
   * @return string the HTTP content type for the current response.
   * @access private
   */
  function getHTTPContentType() {
    return 'text/xml';
  }

  /**
   * gets the HTTP content type charset for the current response.
   * returns false for non-text content types.
   *
   * Note: getHTTPBody must be called before this.
   *
   * @return string the HTTP content type charset for the current response.
   * @access private
   */
  function getHTTPContentTypeCharset() {
    return $this->soap_defencoding;
  }

  /**
   * add a method to the dispatch map (this has been replaced by the register method)
   *
   * @param    string $methodname
   * @param    string $in array of input values
   * @param    string $out array of output values
   * @access   public
   * @deprecated
   */
  function add_to_map($methodname, $in, $out) {
    $this->operations[$methodname] = array(
      'name' => $methodname,
      'in' => $in,
      'out' => $out,
    );
  }

  /**
   * register a service function with the server
   *
   * @param    string $name the name of the PHP function, class.method or class..method
   * @param    array $in assoc array of input values: key = param name, value = param type
   * @param    array $out assoc array of output values: key = param name, value = param type
   * @param	mixed $namespace the element namespace for the method or false
   * @param	mixed $soapaction the soapaction for the method or false
   * @param	mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
   * @param	mixed $use optional (encoded|literal) or false
   * @param	string $documentation optional Description to include in WSDL
   * @param	string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
   * @access   public
   */
  function register($name, $in = [], $out = [], $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '') {
    global $HTTP_SERVER_VARS;
    if ($this->externalWSDLURL) {
      die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
    }
    if (!$name) {
      die('You must specify a name when you register an operation');
    }
    if (!is_array($in)) {
      die('You must provide an array for operation inputs');
    }
    if (!is_array($out)) {
      die('You must provide an array for operation outputs');
    }
    if (false == $namespace) {
    }
    if (false == $soapaction) {
      if (isset($_SERVER)) {
        $SERVER_NAME = $_SERVER['SERVER_NAME'];
        $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
      }
      elseif (isset($HTTP_SERVER_VARS)) {
        $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
        $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
      }
      else {
        $this
          ->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
      }
      $soapaction = "http://{$SERVER_NAME}{$SCRIPT_NAME}/{$name}";
    }
    if (false == $style) {
      $style = "rpc";
    }
    if (false == $use) {
      $use = "encoded";
    }
    if ($use == 'encoded' && ($encodingStyle = '')) {
      $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
    }
    $this->operations[$name] = array(
      'name' => $name,
      'in' => $in,
      'out' => $out,
      'namespace' => $namespace,
      'soapaction' => $soapaction,
      'style' => $style,
    );
    if ($this->wsdl) {
      $this->wsdl
        ->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
    }
    return true;
  }

  /**
   * Specify a fault to be returned to the client.
   * This also acts as a flag to the server that a fault has occured.
   *
   * @param	string $faultcode
   * @param	string $faultstring
   * @param	string $faultactor
   * @param	string $faultdetail
   * @access   public
   */
  function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '') {
    if ($faultdetail == '' && $this->debug_flag) {
      $faultdetail = $this
        ->getDebug();
    }
    $this->fault = new soap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
    $this->fault->soap_defencoding = $this->soap_defencoding;
  }

  /**
   * Sets up wsdl object.
   * Acts as a flag to enable internal WSDL generation
   *
   * @param string $serviceName, name of the service
   * @param mixed $namespace optional 'tns' service namespace or false
   * @param mixed $endpoint optional URL of service endpoint or false
   * @param string $style optional (rpc|document) WSDL style (also specified by operation)
   * @param string $transport optional SOAP transport
   * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
   */
  function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) {
    global $HTTP_SERVER_VARS;
    if (isset($_SERVER)) {
      $SERVER_NAME = $_SERVER['SERVER_NAME'];
      $SERVER_PORT = $_SERVER['SERVER_PORT'];
      $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
      $HTTPS = $_SERVER['HTTPS'];
    }
    elseif (isset($HTTP_SERVER_VARS)) {
      $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
      $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
      $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
      $HTTPS = $HTTP_SERVER_VARS['HTTPS'];
    }
    else {
      $this
        ->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
    }
    if ($SERVER_PORT == 80) {
      $SERVER_PORT = '';
    }
    else {
      $SERVER_PORT = ':' . $SERVER_PORT;
    }
    if (false == $namespace) {
      $namespace = "http://{$SERVER_NAME}/soap/{$serviceName}";
    }
    if (false == $endpoint) {
      if ($HTTPS == '1' || $HTTPS == 'on') {
        $SCHEME = 'https';
      }
      else {
        $SCHEME = 'http';
      }
      $endpoint = "{$SCHEME}://{$SERVER_NAME}{$SERVER_PORT}{$SCRIPT_NAME}";
    }
    if (false == $schemaTargetNamespace) {
      $schemaTargetNamespace = $namespace;
    }
    $this->wsdl = new wsdl();
    $this->wsdl->serviceName = $serviceName;
    $this->wsdl->endpoint = $endpoint;
    $this->wsdl->namespaces['tns'] = $namespace;
    $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
    $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
    if ($schemaTargetNamespace != $namespace) {
      $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
    }
    $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
    $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
    $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array(
      'location' => '',
      'loaded' => true,
    );
    $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array(
      'location' => '',
      'loaded' => true,
    );
    $this->wsdl->bindings[$serviceName . 'Binding'] = array(
      'name' => $serviceName . 'Binding',
      'style' => $style,
      'transport' => $transport,
      'portType' => $serviceName . 'PortType',
    );
    $this->wsdl->ports[$serviceName . 'Port'] = array(
      'binding' => $serviceName . 'Binding',
      'location' => $endpoint,
      'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/',
    );
  }

}

Members

Namesort descending Modifiers Type Description Overrides
soap_server::$debug_flag property * whether to append debug to response as XML comment * * @access public
soap_server::$decode_utf8 property * toggles whether the parser decodes element content w/ utf8_decode() * * @access public
soap_server::$document property * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text) * * @access public
soap_server::$externalWSDLURL property * URL for WSDL (if one) * * @access private
soap_server::$fault property * SOAP fault for response (or false) * * @access private
soap_server::$headers property * HTTP headers of request * * @access private
soap_server::$methodname property * name of method requested * * @access private
soap_server::$methodparams property * method parameters from request * * @access private
soap_server::$methodreturn property * method return value to place in response * * @access private
soap_server::$methodreturnisliteralxml property * whether $methodreturn is a string of literal XML * * @access public
soap_server::$methodURI property * requested method namespace URI * * @access private
soap_server::$operations property * assoc array of operations => opData; operations are added by the register() * method or by parsing an external WSDL definition * * @access private
soap_server::$outgoing_headers property * HTTP headers of response * * @access public
soap_server::$request property * HTTP request * * @access private
soap_server::$requestHeaders property * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text) * * @access public
soap_server::$requestSOAP property * SOAP payload for request (text) * * @access public
soap_server::$response property * HTTP response * * @access private
soap_server::$responseHeaders property * SOAP headers for response (text) * * @access public
soap_server::$responseSOAP property * SOAP payload for response (text) * * @access private
soap_server::$result property * text indication of result (for debugging) * * @access private
soap_server::$SOAPAction property * SOAP Action from request * * @access private
soap_server::$wsdl property * wsdl instance (if one) * * @access private
soap_server::$xml_encoding property * character set encoding of incoming (request) messages * * @access public
soap_server::add_to_map function * add a method to the dispatch map (this has been replaced by the register method) * *
soap_server::configureWSDL function Sets up wsdl object. Acts as a flag to enable internal WSDL generation
soap_server::fault function * Specify a fault to be returned to the client. * This also acts as a flag to the server that a fault has occured. * *
soap_server::getHTTPBody function * gets the HTTP body for the current response. * *
soap_server::getHTTPContentType function * gets the HTTP content type for the current response. * * Note: getHTTPBody must be called before this. * *
soap_server::getHTTPContentTypeCharset function * gets the HTTP content type charset for the current response. * returns false for non-text content types. * * Note: getHTTPBody must be called before this. * *
soap_server::invoke_method function * invokes a PHP function for the requested SOAP method * * The following fields are set by this function (when successful) * * methodreturn * * Note that the PHP function that is called may also set the following * fields to affect the response…
soap_server::parseRequest function * processes SOAP message received from client * *
soap_server::parse_http_headers function * parses HTTP request headers. * * The following fields are set by this function (when successful) * * headers * request * xml_encoding * SOAPAction * * @access private
soap_server::parse_request function * parses a request * * The following fields are set by this function (when successful) * * headers * request * xml_encoding * SOAPAction * request * requestSOAP * methodURI * methodname * methodparams * requestHeaders * document * *…
soap_server::register function * register a service function with the server * *
soap_server::send_response function * sends an HTTP response * * The following fields are set by this function (when successful) * * outgoing_headers * response * * @access private
soap_server::serialize_return function * serializes the return value from a PHP function into a full SOAP Envelope * * The following fields are set by this function (when successful) * * responseSOAP * * This sets the fault field on error * * @access private
soap_server::service function * processes request and returns response * *
soap_server::soap_server function * constructor the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. *
soap_server::verify_method function * takes the value that was created by parsing the request * and compares to the method's signature, if available. * *