home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / SOAP / Server.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  28.5 KB  |  763 lines

  1. <?php
  2. /**
  3.  * This file contains the code for the SOAP server.
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 2.02 of the PHP license,
  8.  * that is bundled with this package in the file LICENSE, and is available at
  9.  * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
  10.  * did not receive a copy of the PHP license and are unable to obtain it
  11.  * through the world-wide-web, please send a note to license@php.net so we can
  12.  * mail you a copy immediately.
  13.  *
  14.  * @category   Web Services
  15.  * @package    SOAP
  16.  * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
  17.  * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
  18.  * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
  19.  * @author     Jan Schneider <jan@horde.org>       Maintenance
  20.  * @copyright  2003-2005 The PHP Group
  21.  * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
  22.  * @link       http://pear.php.net/package/SOAP
  23.  */
  24.  
  25. require_once 'SOAP/Base.php';
  26. require_once 'SOAP/Fault.php';
  27. require_once 'SOAP/Parser.php';
  28. require_once 'SOAP/Value.php';
  29. require_once 'SOAP/WSDL.php';
  30.  
  31. /**
  32.  * SOAP Server Class
  33.  *
  34.  * Originaly based on SOAPx4 by Dietrich Ayala
  35.  * http://dietrich.ganx4.com/soapx4
  36.  *
  37.  * @access   public
  38.  * @package  SOAP
  39.  * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
  40.  * @author   Dietrich Ayala <dietrich@ganx4.com> Original Author
  41.  */
  42. class SOAP_Server extends SOAP_Base
  43. {
  44.     /**
  45.      *
  46.      * @var array
  47.      */
  48.     var $dispatch_map = array(); // create empty dispatch map
  49.     var $dispatch_objects = array();
  50.     var $soapobject = null;
  51.     var $call_methodname = null;
  52.     var $callHandler = null;
  53.     var $callValidation = true;
  54.  
  55.     /**
  56.      * A list of headers that are going to be sent back to the client.
  57.      *
  58.      * @var array
  59.      */
  60.     var $headers = array();
  61.  
  62.     /**
  63.      *
  64.      * @var string
  65.      */
  66.     var $request = '';
  67.  
  68.     /**
  69.      *
  70.      * @var string  XML-Encoding
  71.      */
  72.     var $xml_encoding = SOAP_DEFAULT_ENCODING;
  73.     var $response_encoding = 'UTF-8';
  74.  
  75.     var $result = 'successful'; // for logging interop results to db
  76.  
  77.     var $endpoint = ''; // the uri to ME!
  78.  
  79.     var $service = ''; //soapaction header
  80.     var $method_namespace = null;
  81.     var $__options = array('use' => 'encoded',
  82.                            'style' => 'rpc',
  83.                            'parameters' => 0);
  84.  
  85.     function SOAP_Server($options = null)
  86.     {
  87.         ini_set('track_errors', 1);
  88.         parent::SOAP_Base('Server');
  89.  
  90.         if (is_array($options)) {
  91.             if (isset($options['use'])) {
  92.                 $this->__options['use'] = $options['use'];
  93.             }
  94.             if (isset($options['style'])) {
  95.                 $this->__options['style'] = $options['style'];
  96.             }
  97.             if (isset($options['parameters'])) {
  98.                 $this->__options['parameters'] = $options['parameters'];
  99.             }
  100.         }
  101.         // assume we encode with section 5
  102.         $this->_section5 = true;
  103.         if ($this->__options['use']=='literal') {
  104.             $this->_section5 = false;
  105.         }
  106.     }
  107.  
  108.     /**
  109.      * Error handler for errors that happen in proxied methods.
  110.      *
  111.      * To always return a valid SOAP response even on errors that don't happen
  112.      * in this code, the errors are catched, transformed to a SOAP fault and
  113.      * immediately sent to the client.
  114.      *
  115.      * @see http://www.php.net/set_error_handler
  116.      */
  117.     function _errorHandler($errno, $errmsg, $filename, $linenum, $vars)
  118.     {
  119.         /* The error handler should ignore '0' errors, eg. hidden by @ - see
  120.          * the set_error_handler manual page. (thanks to Alan Knowles). */
  121.         if (!$errno || $errno == E_NOTICE) {
  122.             return;
  123.         }
  124.  
  125.         $this->fault =& new SOAP_Fault($errmsg, 'Server', 'PHP', "Errno: $errno\nFilename: $filename\nLineno: $linenum\n");
  126.  
  127.         $this->_sendResponse();
  128.         exit;
  129.     }
  130.  
  131.     function _getContentEncoding($content_type)
  132.     {
  133.         /* Get the character encoding of the incoming request treat incoming
  134.          * data as UTF-8 if no encoding set. */
  135.         $this->xml_encoding = 'UTF-8';
  136.         if (strpos($content_type, '=')) {
  137.             $enc = strtoupper(str_replace('"', '', substr(strstr($content_type, '='), 1)));
  138.             if (!in_array($enc, $this->_encodings)) {
  139.                 return false;
  140.             }
  141.             $this->xml_encoding = $enc;
  142.         }
  143.  
  144.         return true;
  145.     }
  146.  
  147.  
  148.     /**
  149.      * Parses request and posts response.
  150.      */
  151.     function service($data, $endpoint = '', $test = false)
  152.     {
  153.         $response = null;
  154.         $attachments = array();
  155.         $useEncoding = 'DIME';
  156.  
  157.         /* Figure out our endpoint. */
  158.         $this->endpoint = $endpoint;
  159.         if (!$test && !$this->endpoint) {
  160.             /* We'll try to build our endpoint. */
  161.             $this->endpoint = 'http://' . $_SERVER['SERVER_NAME'];
  162.             if ($_SERVER['SERVER_PORT']) {
  163.                 $this->endpoint .= ':' . $_SERVER['SERVER_PORT'];
  164.             }
  165.             $this->endpoint .= $_SERVER['SCRIPT_NAME'];
  166.         }
  167.  
  168.         /* Get the character encoding of the incoming request treat incoming
  169.          * data as UTF-8 if no encoding set. */
  170.         if (isset($_SERVER['CONTENT_TYPE'])) {
  171.             if (strcasecmp($_SERVER['CONTENT_TYPE'], 'application/dime') == 0) {
  172.                 $this->_decodeDIMEMessage($data, $headers, $attachments);
  173.                 $useEncoding = 'DIME';
  174.             } elseif (stristr($_SERVER['CONTENT_TYPE'], 'multipart/related')) {
  175.                 /* This is a mime message, let's decode it. */
  176.                 $data = 'Content-Type: ' .
  177.                     stripslashes($_SERVER['CONTENT_TYPE']) .
  178.                     "\r\n\r\n" . $data;
  179.                 $this->_decodeMimeMessage($data, $headers, $attachments);
  180.                 $useEncoding = 'Mime';
  181.             }
  182.             if (!isset($headers['content-type'])) {
  183.                 $headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']);
  184.             }
  185.             if (!$this->fault &&
  186.                 !$this->_getContentEncoding($headers['content-type'])) {
  187.                 $this->xml_encoding = SOAP_DEFAULT_ENCODING;
  188.                 /* Found encoding we don't understand; return a fault. */
  189.                 $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server');
  190.             }
  191.         }
  192.  
  193.         /* If this is not a POST with Content-Type text/xml, try to return a
  194.          * WSDL file. */
  195.         if (!$this->fault && !$test &&
  196.             ($_SERVER['REQUEST_METHOD'] != 'POST' ||
  197.              strncmp($headers['content-type'], 'text/xml', 8) != 0)) {
  198.             /* This is not possibly a valid SOAP request, try to return a WSDL
  199.              * file. */
  200.             $this->_raiseSoapFault('Invalid SOAP request, must be POST with content-type: text/xml, got: ' . (isset($headers['content-type']) ? $headers['content-type'] : 'Nothing!'), '', '', 'Server');
  201.         }
  202.  
  203.         if (!$this->fault) {
  204.             /* $response is a SOAP_Msg object. */
  205.             $soap_msg = $this->parseRequest($data, $attachments);
  206.  
  207.             /* Handle Mime or DIME encoding. */
  208.             /* TODO: DIME decoding should move to the transport, do it here
  209.              * for now and for ease of getting it done. */
  210.             if (count($this->__attachments)) {
  211.                 if ($useEncoding == 'Mime') {
  212.                     $soap_msg = $this->_makeMimeMessage($soap_msg);
  213.                 } else {
  214.                     // default is dime
  215.                     $soap_msg = $this->_makeDIMEMessage($soap_msg);
  216.                     $this->headers['Content-Type'] = 'application/dime';
  217.                 }
  218.                 if (PEAR::isError($soap_msg)) {
  219.                     return $this->raiseSoapFault($soap_msg);
  220.                 }
  221.             }
  222.  
  223.             if (is_array($soap_msg)) {
  224.                 $response = $soap_msg['body'];
  225.                 if (count($soap_msg['headers'])) {
  226.                     $this->headers = $soap_msg['headers'];
  227.                 }
  228.             } else {
  229.                 $response = $soap_msg;
  230.             }
  231.         }
  232.  
  233.         $this->_sendResponse($response);
  234.     }
  235.  
  236.     /**
  237.      * Sends the final HTTP response to the client, including the HTTP header
  238.      * and the HTTP body.
  239.      *
  240.      * If an error happened, it returns a SOAP fault instead of the response
  241.      * body.
  242.      *
  243.      * @param string $response  The response body.
  244.      */
  245.     function _sendResponse($response = '')
  246.     {
  247.         /* Make distinction between the different SAPIs, running PHP as CGI or
  248.          * as a module. */
  249.         if (stristr(php_sapi_name(), 'cgi') === 0) {
  250.             $hdrs_type = 'Status:';
  251.         } else {
  252.             $hdrs_type = 'HTTP/1.1';
  253.         }
  254.  
  255.         if ($this->fault) {
  256.             $hdrs = "$hdrs_type 500 Soap Fault\r\n";
  257.             $response = $this->fault->message();
  258.         } else {
  259.             $hdrs = "$hdrs_type 200 OK\r\n";
  260.         }
  261.         header($hdrs);
  262.  
  263.         $this->headers['Server'] = SOAP_LIBRARY_NAME;
  264.         if (!isset($this->headers['Content-Type'])) {
  265.             $this->headers['Content-Type'] = 'text/xml; charset=' .
  266.                 $this->response_encoding;
  267.         }
  268.         $this->headers['Content-Length'] = strlen($response);
  269.  
  270.         foreach ($this->headers as $k => $v) {
  271.             header("$k: $v");
  272.             $hdrs .= "$k: $v\r\n";
  273.         }
  274.  
  275.         $this->response = $hdrs . "\r\n" . $response;
  276.         print $response;
  277.     }
  278.  
  279.     function &callMethod($methodname, &$args)
  280.     {
  281.         if ($this->callHandler) {
  282.             return @call_user_func_array($this->callHandler, array($methodname, $args));
  283.         }
  284.  
  285.         set_error_handler(array($this, '_errorHandler'));
  286.  
  287.         if ($args) {
  288.             /* Call method with parameters. */
  289.             if (isset($this->soapobject) && is_object($this->soapobject)) {
  290.                 $ret = @call_user_func_array(array(&$this->soapobject, $methodname), $args);
  291.             } else {
  292.                 $ret = @call_user_func_array($methodname, $args);
  293.             }
  294.         } else {
  295.             /* Call method withour parameters. */
  296.             if (is_object($this->soapobject)) {
  297.                 $ret = @call_user_func(array(&$this->soapobject, $methodname));
  298.             } else {
  299.                 $ret = @call_user_func($methodname);
  300.             }
  301.         }
  302.  
  303.         restore_error_handler();
  304.  
  305.         return $ret;
  306.     }
  307.  
  308.     /**
  309.      * Creates SOAP_Value object with return values from method.
  310.      * Uses method signature to determine type.
  311.      */
  312.     function buildResult(&$method_response, &$return_type,
  313.                          $return_name = 'return', $namespace = '')
  314.     {
  315.         if (is_a($method_response, 'SOAP_Value')) {
  316.             $return_val = array($method_response);
  317.         } else {
  318.             if (is_array($return_type) && is_array($method_response)) {
  319.                 $i = 0;
  320.  
  321.                 foreach ($return_type as $key => $type) {
  322.                     if (is_numeric($key)) {
  323.                         $key = 'item';
  324.                     }
  325.                     if (is_a($method_response[$i],'soap_value')) {
  326.                         $return_val[] = $method_response[$i++];
  327.                     } else {
  328.                         $qn =& new QName($key, $namespace);
  329.                         $return_val[] =& new SOAP_Value($qn->fqn(), $type, $method_response[$i++]);
  330.                     }
  331.                 }
  332.             } else {
  333.                 if (is_array($return_type)) {
  334.                     $keys = array_keys($return_type);
  335.                     if (!is_numeric($keys[0])) {
  336.                         $return_name = $keys[0];
  337.                     }
  338.                     $values = array_values($return_type);
  339.                     $return_type = $values[0];
  340.                 }
  341.                 $qn =& new QName($return_name, $namespace);
  342.                 $return_val = array();
  343.                 $return_val[] =& new SOAP_Value($qn->fqn(), $return_type, $method_response);
  344.             }
  345.         }
  346.         return $return_val;
  347.     }
  348.  
  349.     function parseRequest($data = '', $attachments = null)
  350.     {
  351.         /* Parse response, get SOAP_Parser object. */
  352.         $parser =& new SOAP_Parser($data, $this->xml_encoding, $attachments);
  353.         /* If fault occurred during message parsing. */
  354.         if ($parser->fault) {
  355.             $this->fault = $parser->fault;
  356.             return null;
  357.         }
  358.  
  359.         /* Handle message headers. */
  360.         $request_headers = $parser->getHeaders();
  361.         $header_results = array();
  362.  
  363.         if ($request_headers) {
  364.             if (!is_a($request_headers, 'SOAP_Value')) {
  365.                 $this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_headers, '', '', 'Server');
  366.                 return null;
  367.             }
  368.             if ($request_headers->value) {
  369.                 /* Handle headers now. */
  370.                 foreach ($request_headers->value as $header_val) {
  371.                     $f_exists = $this->validateMethod($header_val->name, $header_val->namespace);
  372.  
  373.                     /* TODO: this does not take into account message routing
  374.                      * yet. */
  375.                     $myactor = !$header_val->actor ||
  376.                         $header_val->actor == 'http://schemas.xmlsoap.org/soap/actor/next' ||
  377.                         $header_val->actor == $this->endpoint;
  378.  
  379.                     if (!$f_exists && $header_val->mustunderstand && $myactor) {
  380.                         $this->_raiseSoapFault('I don\'t understand header ' . $header_val->name, '', '', 'MustUnderstand');
  381.                         return null;
  382.                     }
  383.  
  384.                     /* We only handle the header if it's for us. */
  385.                     $isok = $f_exists && $myactor;
  386.  
  387.                     if ($isok) {
  388.                         /* Call our header now! */
  389.                         $header_method = $header_val->name;
  390.                         $header_data = array($this->_decode($header_val));
  391.                         /* If there are parameters to pass. */
  392.                         $hr =& $this->callMethod($header_method, $header_data);
  393.                         $header_results[] = array_shift($this->buildResult($hr, $this->return_type, $header_method, $header_val->namespace));
  394.                     }
  395.                 }
  396.             }
  397.         }
  398.  
  399.         /* Handle the method call. */
  400.         /* Evaluate message, getting back a SOAP_Value object. */
  401.         $this->call_methodname = $this->methodname = $parser->root_struct_name[0];
  402.  
  403.         /* Figure out the method namespace. */
  404.         $this->method_namespace = $parser->message[$parser->root_struct[0]]['namespace'];
  405.  
  406.         if ($this->_wsdl) {
  407.             $this->_setSchemaVersion($this->_wsdl->xsd);
  408.             $dataHandler = $this->_wsdl->getDataHandler($this->methodname, $this->method_namespace);
  409.             if ($dataHandler)
  410.                 $this->call_methodname = $this->methodname = $dataHandler;
  411.  
  412.             $this->_portName = $this->_wsdl->getPortName($this->methodname);
  413.             if (PEAR::isError($this->_portName)) {
  414.                 return $this->_raiseSoapFault($this->_portName);
  415.             }
  416.             $opData = $this->_wsdl->getOperationData($this->_portName, $this->methodname);
  417.             if (PEAR::isError($opData)) {
  418.                 return $this->_raiseSoapFault($opData);
  419.             }
  420.             $this->__options['style'] = $opData['style'];
  421.             $this->__options['use'] = $opData['output']['use'];
  422.             $this->__options['parameters'] = $opData['parameters'];
  423.         }
  424.  
  425.         /* Does method exist? */
  426.         if (!$this->methodname ||
  427.             !$this->validateMethod($this->methodname, $this->method_namespace)) {
  428.             $this->_raiseSoapFault('method "' . $this->method_namespace . $this->methodname . '" not defined in service', '', '', 'Server');
  429.             return null;
  430.         }
  431.  
  432.         if (!$request_val = $parser->getResponse()) {
  433.             return null;
  434.         }
  435.         if (!is_a($request_val, 'SOAP_Value')) {
  436.             $this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_val, '', '', 'Server');
  437.             return null;
  438.         }
  439.  
  440.         /* Verify that SOAP_Value objects in request match the methods
  441.          * signature. */
  442.         if (!$this->verifyMethod($request_val)) {
  443.             /* verifyMethod() creates the fault. */
  444.             return null;
  445.         }
  446.  
  447.         /* Need to set special error detection inside the value class to
  448.          * differentiate between no params passed, and an error decoding. */
  449.         $request_data = $this->__decodeRequest($request_val);
  450.         if (PEAR::isError($request_data)) {
  451.             return $this->_raiseSoapFault($request_data);
  452.         }
  453.         $method_response =& $this->callMethod($this->call_methodname, $request_data);
  454.  
  455.         if ($this->__options['parameters'] ||
  456.             !$method_response ||
  457.             $this->__options['style']=='rpc') {
  458.             /* Get the method result. */
  459.             if (is_null($method_response)) {
  460.                 $return_val = null;
  461.             } else {
  462.                 $return_val = $this->buildResult($method_response, $this->return_type);
  463.             }
  464.  
  465.             $qn =& new QName($this->methodname . 'Response', $this->method_namespace);
  466.             $methodValue =& new SOAP_Value($qn->fqn(), 'Struct', $return_val);
  467.         } else {
  468.             $methodValue =& $method_response;
  469.         }
  470.         return $this->_makeEnvelope($methodValue, $header_results, $this->response_encoding);
  471.     }
  472.  
  473.     function &__decodeRequest($request, $shift = false)
  474.     {
  475.         if (!$request) {
  476.             return null;
  477.         }
  478.  
  479.         /* Check for valid response. */
  480.         if (PEAR::isError($request)) {
  481.             return $this->_raiseSoapFault($request);
  482.         } else if (!is_a($request, 'SOAP_Value')) {
  483.             return $this->_raiseSoapFault('Invalid data in server::__decodeRequest');
  484.         }
  485.  
  486.         /* Decode to native php datatype. */
  487.         $requestArray = $this->_decode($request);
  488.         /* Fault? */
  489.         if (PEAR::isError($requestArray)) {
  490.             return $this->_raiseSoapFault($requestArray);
  491.         }
  492.         if (is_object($requestArray) &&
  493.             get_class($requestArray) == 'stdClass') {
  494.             $requestArray = get_object_vars($requestArray);
  495.         } elseif ($this->__options['style'] == 'document') {
  496.             $requestArray = array($requestArray);
  497.         }
  498.         if (is_array($requestArray)) {
  499.             if (isset($requestArray['faultcode']) ||
  500.                 isset($requestArray['SOAP-ENV:faultcode'])) {
  501.                 $faultcode = $faultstring = $faultdetail = $faultactor = '';
  502.                 foreach ($requestArray as $k => $v) {
  503.                     if (stristr($k, 'faultcode')) {
  504.                         $faultcode = $v;
  505.                     }
  506.                     if (stristr($k, 'faultstring')) {
  507.                         $faultstring = $v;
  508.                     }
  509.                     if (stristr($k, 'detail')) {
  510.                         $faultdetail = $v;
  511.                     }
  512.                     if (stristr($k, 'faultactor')) {
  513.                         $faultactor = $v;
  514.                     }
  515.                 }
  516.                 return $this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
  517.             }
  518.             /* Return array of return values. */
  519.             if ($shift && count($requestArray) == 1) {
  520.                 return array_shift($requestArray);
  521.             }
  522.             return $requestArray;
  523.         }
  524.         return $requestArray;
  525.     }
  526.  
  527.     function verifyMethod($request)
  528.     {
  529.         if (!$this->callValidation) {
  530.             return true;
  531.         }
  532.  
  533.         $params = $request->value;
  534.  
  535.         /* Get the dispatch map if one exists. */
  536.         $map = null;
  537.         if (array_key_exists($this->methodname, $this->dispatch_map)) {
  538.             $map = $this->dispatch_map[$this->methodname];
  539.         } elseif (isset($this->soapobject)) {
  540.             if (method_exists($this->soapobject, '__dispatch')) {
  541.                 $map = $this->soapobject->__dispatch($this->methodname);
  542.             } elseif (method_exists($this->soapobject, $this->methodname)) {
  543.                 /* No map, all public functions are SOAP functions. */
  544.                 return true;
  545.             }
  546.         }
  547.         if (!$map) {
  548.             $this->_raiseSoapFault('SOAP request specified an unhandled method "' . $this->methodname . '"', '', '', 'Client');
  549.             return false;
  550.         }
  551.  
  552.         /* If we aliased the SOAP method name to a PHP function, change
  553.          * call_methodname so we do the right thing. */
  554.         if (array_key_exists('alias', $map) && !empty($map['alias'])) {
  555.             $this->call_methodname = $map['alias'];
  556.         }
  557.  
  558.         /* If there are input parameters required. */
  559.         if ($sig = $map['in']) {
  560.             $this->input_value = count($sig);
  561.             $this->return_type = is_array($map['out']) ? $map['out'] : false;
  562.             if (is_array($params)) {
  563.                 /* Validate the number of parameters. */
  564.                 if (count($params) == count($sig)) {
  565.                     /* Make array of param types. */
  566.                     foreach ($params as $param) {
  567.                         $p[] = strtolower($param->type);
  568.                     }
  569.                     $sig_t = array_values($sig);
  570.                     /* Validate each param's type. */
  571.                     for ($i = 0; $i < count($p); $i++) {
  572.                         /* If SOAP types do not match, it's still fine if the
  573.                          * mapped php types match this allows using plain PHP
  574.                          * variables to work (i.e. stuff like Decimal would
  575.                          * fail otherwise). We consider this only error if the
  576.                          * types exist in our type maps, and they differ. */
  577.                         if (strcasecmp($sig_t[$i], $p[$i]) != 0 &&
  578.                             isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]]) &&
  579.                             strcasecmp($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]], $this->_typemap[SOAP_XML_SCHEMA_VERSION][$p[$i]]) != 0) {
  580.  
  581.                             $param = $params[$i];
  582.                             $this->_raiseSoapFault("SOAP request contained mismatching parameters of name $param->name had type [{$p[$i]}], which did not match signature's type: [{$sig_t[$i]}], matched? " . (strcasecmp($sig_t[$i], $p[$i])), '', '', 'Client');
  583.                             return false;
  584.                         }
  585.                     }
  586.                     return true;
  587.                 } else {
  588.                     /* Wrong number of params. */
  589.                     $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" required ' . count($sig) . ' and request provided ' . count($params), '', '', 'Client');
  590.                     return false;
  591.                 }
  592.             } else {
  593.                 /* No params. */
  594.                 $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" requires ' . count($sig) . ' parameters, and request provided none.', '', '', 'Client');
  595.                 return false;
  596.             }
  597.         }
  598.  
  599.         /* We'll try it anyway. */
  600.         return true;
  601.     }
  602.  
  603.     function validateMethod($methodname, $namespace = null)
  604.     {
  605.         unset($this->soapobject);
  606.  
  607.         if (!$this->callValidation) {
  608.             return true;
  609.         }
  610.  
  611.         /* No SOAP access to private functions. */
  612.         if ($methodname[0] == '_') {
  613.             return false;
  614.         }
  615.  
  616.         /* if it's in our function list, ok */
  617.         if (array_key_exists($methodname, $this->dispatch_map) &&
  618.             (!$namespace ||
  619.              !array_key_exists('namespace', $this->dispatch_map[$methodname]) ||
  620.              $namespace == $this->dispatch_map[$methodname]['namespace'])) {
  621.             if (array_key_exists('namespace', $this->dispatch_map[$methodname]))
  622.                 $this->method_namespace = $this->dispatch_map[$methodname]['namespace'];
  623.             return true;
  624.         }
  625.  
  626.         /* if it's in an object, it's ok */
  627.         if (isset($this->dispatch_objects[$namespace])) {
  628.             $c = count($this->dispatch_objects[$namespace]);
  629.             for ($i = 0; $i < $c; $i++) {
  630.                 $obj =& $this->dispatch_objects[$namespace][$i];
  631.                 /* If we have a dispatch map, and the function is not in the
  632.                  * dispatch map, then it is not callable! */
  633.                 if (method_exists($obj, '__dispatch')) {
  634.                     if ($obj->__dispatch($methodname)) {
  635.                         $this->method_namespace = $namespace;
  636.                         $this->soapobject =& $obj;
  637.                         return true;
  638.                     }
  639.                 } elseif (method_exists($obj, $methodname)) {
  640.                     $this->method_namespace = $namespace;
  641.                     $this->soapobject =& $obj;
  642.                     return true;
  643.                 }
  644.             }
  645.         }
  646.  
  647.         return false;
  648.     }
  649.  
  650.     function addObjectMap(&$obj, $namespace = null, $service_name = 'Default',
  651.                           $service_desc = '')
  652.     {
  653.         if (!$namespace) {
  654.             if (isset($obj->namespace)) {
  655.                 // XXX a bit of backwards compatibility
  656.                 $namespace = $obj->namespace;
  657.             } else {
  658.                 $this->_raiseSoapFault('No namespace provided for class!', '', '', 'Server');
  659.                 return false;
  660.             }
  661.         }
  662.         if (!isset($this->dispatch_objects[$namespace])) {
  663.             $this->dispatch_objects[$namespace] = array();
  664.         }
  665.         $this->dispatch_objects[$namespace][] =& $obj;
  666.  
  667.         // Create internal WSDL structures for object
  668.  
  669.         // XXX Because some internal workings of PEAR::SOAP decide whether to
  670.         // do certain things by the presence or absence of _wsdl, we should
  671.         // only create a _wsdl structure if we know we can fill it; if
  672.         // __dispatch_map or __typedef for the object is missing, we should
  673.         // avoid creating it. Later, when we are using PHP 5 introspection, we
  674.         // will be able to make the data for all objects without any extra
  675.         // information from the developers, and this condition should be
  676.         // dropped.
  677.  
  678.         // XXX Known issue: if imported WSDL (bindWSDL) or another WSDL source
  679.         // is used to add _wsdl structure information, then addObjectWSDL is
  680.         // used, there is a high possibility of _wsdl data corruption;
  681.         // therefore you should avoid using __dispatch_map/__typedef
  682.         // definitions AND other WSDL data sources in the same service. We
  683.         // exclude classes that don't have __typedefs to allow external WSDL
  684.         // files to be used with classes with no internal type definitions
  685.         // (the types are defined in the WSDL file). When addObjectWSDL is
  686.         // refactored to not cause corruption, this restriction can be
  687.         // relaxed.
  688.  
  689.         // In summary, if you add an object with both a dispatch map and type
  690.         // definitions, then previous WSDL file operation and type definitions
  691.         // will be overwritten.
  692.         if (isset($obj->__dispatch_map) && isset($obj->__typedef)) {
  693.             $this->addObjectWSDL($obj, $namespace, $service_name, $service_desc);
  694.         }
  695.  
  696.         return true;
  697.     }
  698.  
  699.     /**
  700.      * Adds a method to the dispatch map.
  701.      */
  702.     function addToMap($methodname, $in, $out, $namespace = null, $alias = null)
  703.     {
  704.         if (!function_exists($methodname)) {
  705.             $this->_raiseSoapFault('Error mapping function', '', '', 'Server');
  706.             return false;
  707.         }
  708.  
  709.         $this->dispatch_map[$methodname]['in'] = $in;
  710.         $this->dispatch_map[$methodname]['out'] = $out;
  711.         $this->dispatch_map[$methodname]['alias'] = $alias;
  712.         if ($namespace) {
  713.             $this->dispatch_map[$methodname]['namespace'] = $namespace;
  714.         }
  715.  
  716.         return true;
  717.     }
  718.  
  719.     function setCallHandler($callHandler, $validation = true)
  720.     {
  721.         $this->callHandler = $callHandler;
  722.         $this->callValidation = $validation;
  723.     }
  724.  
  725.     /**
  726.      * @deprecated use bindWSDL from now on
  727.      */
  728.     function bind($wsdl_url)
  729.     {
  730.         $this->bindWSDL($wsdl_url);
  731.     }
  732.  
  733.     /**
  734.      * @param  string a url to a WSDL resource
  735.      * @return void
  736.      */
  737.     function bindWSDL($wsdl_url)
  738.     {
  739.         /* Instantiate WSDL class. */
  740.         $this->_wsdl =& new SOAP_WSDL($wsdl_url);
  741.         if ($this->_wsdl->fault) {
  742.             $this->_raiseSoapFault($this->_wsdl->fault);
  743.         }
  744.     }
  745.  
  746.     /**
  747.      * @return void
  748.      */
  749.     function addObjectWSDL(&$wsdl_obj, $targetNamespace, $service_name,
  750.                            $service_desc = '')
  751.     {
  752.         if (!isset($this->_wsdl)) {
  753.             $this->_wsdl =& new SOAP_WSDL;
  754.         }
  755.  
  756.         $this->_wsdl->parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc);
  757.  
  758.         if ($this->_wsdl->fault) {
  759.             $this->_raiseSoapFault($this->_wsdl->fault);
  760.         }
  761.     }
  762. }
  763.