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