source: branches/version-2_12-dev/data/module/SOAP/Server.php @ 21461

Revision 21461, 31.2 KB checked in by Seasoft, 12 years ago (diff)

#1522 (PEAR::SOAP をバージョンアップ)

  • 0.12.0 -> 0.13.0
  • SOAP_WSDL により、「Yahoo!デベロッパーネットワーク」の「オークション SOAP」を取得できることを確認した。
  • Property svn:eol-style set to LF
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
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
25require_once 'SOAP/Base.php';
26require_once 'SOAP/Fault.php';
27require_once 'SOAP/Parser.php';
28require_once 'SOAP/Value.php';
29require_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 */
42class 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 (isset($_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, $this->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, $this->headers, $attachments);
196                $useEncoding = 'Mime';
197            }
198            if (!isset($this->headers['content-type'])) {
199                $this->headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']);
200            }
201            if (!$this->fault &&
202                !$this->_getContentEncoding($this->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            ((isset($_SERVER['REQUEST_METHOD']) &&
213              $_SERVER['REQUEST_METHOD'] != 'POST') ||
214             (isset($this->headers['content-type']) &&
215              strncmp($this->headers['content-type'], 'text/xml', 8) != 0))) {
216            /* This is not possibly a valid SOAP request, try to return a WSDL
217             * file. */
218            $got = isset($this->headers['content-type']) ? $this->headers['content-type'] : 'Nothing!';
219            $this->_raiseSoapFault('Invalid SOAP request, must be POST with content-type: text/xml, got: ' . $got, '', '', 'Server');
220        }
221
222        if (!$this->fault) {
223            /* $response is a SOAP_Msg object. */
224            $soap_msg = $this->parseRequest($data, $attachments);
225
226            /* Handle Mime or DIME encoding. */
227            /* TODO: DIME decoding should move to the transport, do it here
228             * for now and for ease of getting it done. */
229            if (count($this->_attachments)) {
230                if ($useEncoding == 'Mime') {
231                    $soap_msg = $this->_makeMimeMessage($soap_msg);
232                } else {
233                    // default is dime
234                    $soap_msg = $this->_makeDIMEMessage($soap_msg);
235                    $this->headers['Content-Type'] = 'application/dime';
236                }
237                if (PEAR::isError($soap_msg)) {
238                    return $this->_raiseSoapFault($soap_msg);
239                }
240            }
241
242            if (is_array($soap_msg)) {
243                $response = $soap_msg['body'];
244                if (count($soap_msg['headers'])) {
245                    $this->headers = $soap_msg['headers'];
246                }
247            } else {
248                $response = $soap_msg;
249            }
250        }
251
252        if ($return) {
253            if ($this->fault) {
254                $response = $this->fault->message();
255            }
256            return $response;
257        }
258
259        $this->_sendResponse($response);
260    }
261
262    /**
263     * Sends the final HTTP response to the client, including the HTTP header
264     * and the HTTP body.
265     *
266     * If an error happened, it returns a SOAP fault instead of the response
267     * body.
268     *
269     * @param string $response  The response body.
270     */
271    function _sendResponse($response = '')
272    {
273        /* Make distinction between the different SAPIs, running PHP as CGI or
274         * as a module. */
275        if (stristr(php_sapi_name(), 'cgi') === 0) {
276            $hdrs_type = 'Status:';
277        } else {
278            $hdrs_type = 'HTTP/1.1';
279        }
280
281        if ($this->fault) {
282            $hdrs = $hdrs_type . ' ' . $this->_options['http_status_fault'] . "\r\n";
283            $response = $this->fault->message($this->response_encoding);
284        } else {
285            $hdrs = $hdrs_type . ' ' . $this->_options['http_status_success'] . "\r\n";
286        }
287        header($hdrs);
288
289        $this->headers['Server'] = SOAP_LIBRARY_NAME;
290        if (!isset($this->headers['Content-Type'])) {
291            $this->headers['Content-Type'] = 'text/xml; charset=' .
292                $this->response_encoding;
293        }
294        $this->headers['Content-Length'] = strlen($response);
295
296        foreach ($this->headers as $k => $v) {
297            $v = str_replace(array("\r", "\n"), '', $v);
298            header("$k: $v");
299            $hdrs .= "$k: $v\r\n";
300        }
301
302        $this->response = $hdrs . "\r\n" . $response;
303        print $response;
304    }
305
306    function &callMethod($methodname, &$args)
307    {
308        if ($this->callHandler) {
309            $ret = @call_user_func_array($this->callHandler, array($methodname, $args));
310            return $ret;
311        }
312
313        set_error_handler(array($this, '_errorHandler'));
314
315        if ($args) {
316            /* Call method with parameters. */
317            if (isset($this->soapobject) && is_object($this->soapobject)) {
318                $ret = call_user_func_array(array(&$this->soapobject, $methodname), $args);
319            } else {
320                $ret = call_user_func_array($methodname, $args);
321            }
322        } else {
323            /* Call method withour parameters. */
324            if (is_object($this->soapobject)) {
325                $ret = call_user_func(array(&$this->soapobject, $methodname));
326            } else {
327                $ret = call_user_func($methodname);
328            }
329        }
330
331        restore_error_handler();
332
333        return $ret;
334    }
335
336    /**
337     * Creates SOAP_Value objects with return values from method.
338     * Uses method signature to determine type.
339     *
340     * @param mixed $method_response  The result(s).
341     * @param array|string $type      The type(s) of the return value(s).
342     * @param string $return_name     The name of the return value.
343     * @param string $namespace       The namespace of the return value.
344     *
345     * @return array  List of SOAP_Value objects.
346     */
347    function buildResult(&$method_response, &$return_type,
348                         $return_name = 'return', $namespace = '')
349    {
350        if (is_a($method_response, 'SOAP_Value')) {
351            $return_val = array($method_response);
352        } else {
353            if (is_array($return_type) && is_array($method_response)) {
354                $i = 0;
355
356                foreach ($return_type as $key => $type) {
357                    if (is_numeric($key)) {
358                        $key = 'item';
359                    }
360                    if (is_a($method_response[$i], 'SOAP_Value')) {
361                        $return_val[] =& $method_response[$i++];
362                    } else {
363                        $qn = new QName($key, $namespace);
364                        $return_val[] = new SOAP_Value($qn->fqn(), $type, $method_response[$i++]);
365                    }
366                }
367            } else {
368                if (is_array($return_type)) {
369                    $keys = array_keys($return_type);
370                    if (!is_numeric($keys[0])) {
371                        $return_name = $keys[0];
372                    }
373                    $values = array_values($return_type);
374                    $return_type = $values[0];
375                }
376                $qn = new QName($return_name, $namespace);
377                $return_val = array(new SOAP_Value($qn->fqn(), $return_type, $method_response));
378            }
379        }
380        return $return_val;
381    }
382
383    function parseRequest($data = '', $attachments = null)
384    {
385        /* Parse response, get SOAP_Parser object. */
386        $parser = new SOAP_Parser($data, $this->xml_encoding, $attachments);
387
388        if ($parser->fault) {
389            /* Fault occurred during message parsing. */
390            $this->fault = $parser->fault;
391            return null;
392        }
393        if (!count($parser->root_struct_name)) {
394            /* No method specified. */
395            $this->_raiseSoapFault('No method specified in request.');
396            return null;
397        }
398
399        /* Handle message headers. */
400        $request_headers = $parser->getHeaders();
401        $header_results = array();
402
403        if ($request_headers) {
404            if (!is_a($request_headers, 'SOAP_Value')) {
405                $this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_headers, '', '', 'Server');
406                return null;
407            }
408            if ($request_headers->value) {
409                /* Handle headers now. */
410                foreach ($request_headers->value as $header_val) {
411                    $f_exists = $this->validateMethod($header_val->name, $header_val->namespace);
412
413                    /* TODO: this does not take into account message routing
414                     * yet. */
415                    $myactor = !$header_val->actor ||
416                        $header_val->actor == 'http://schemas.xmlsoap.org/soap/actor/next' ||
417                        $header_val->actor == $this->endpoint;
418
419                    if (!$f_exists && $header_val->mustunderstand && $myactor) {
420                        $this->_raiseSoapFault('I don\'t understand header ' . $header_val->name, '', '', 'MustUnderstand');
421                        return null;
422                    }
423
424                    /* We only handle the header if it's for us. */
425                    $isok = $f_exists && $myactor;
426
427                    if ($isok) {
428                        /* Call our header now! */
429                        $header_method = $header_val->name;
430                        $header_data = array($this->_decode($header_val));
431                        /* If there are parameters to pass. */
432                        $hr =& $this->callMethod($header_method, $header_data);
433                        if (PEAR::isError($hr)) {
434                            $this->_raiseSoapFault($hr);
435                            return null;
436                        }
437                        $results = $this->buildResult($hr, $this->return_type, $header_method, $header_val->namespace);
438                        $header_results[] = $results[0];
439                    }
440                }
441            }
442        }
443
444        /* Handle the method call. */
445        /* Evaluate message, getting back a SOAP_Value object. */
446        $this->call_methodname = $this->methodname = $parser->root_struct_name[0];
447
448        /* Figure out the method namespace. */
449        $this->method_namespace = $parser->message[$parser->root_struct[0]]['namespace'];
450
451        if ($this->_wsdl) {
452            $this->_setSchemaVersion($this->_wsdl->xsd);
453            $dataHandler = $this->_wsdl->getDataHandler($this->methodname, $this->method_namespace);
454            if ($dataHandler)
455                $this->call_methodname = $this->methodname = $dataHandler;
456
457            $this->_portName = $this->_wsdl->getPortName($this->methodname);
458            if (PEAR::isError($this->_portName)) {
459                $this->_raiseSoapFault($this->_portName);
460                return null;
461            }
462            $opData = $this->_wsdl->getOperationData($this->_portName, $this->methodname);
463            if (PEAR::isError($opData)) {
464                $this->_raiseSoapFault($opData);
465                return null;
466            }
467            $this->_options['style'] = $opData['style'];
468            $this->_options['use'] = $opData['output']['use'];
469            $this->_options['parameters'] = $opData['parameters'];
470        }
471
472        /* Does method exist? */
473        if (!$this->methodname ||
474            !$this->validateMethod($this->methodname, $this->method_namespace)) {
475            $this->_raiseSoapFault('method "' . $this->method_namespace . $this->methodname . '" not defined in service', '', '', 'Server');
476            return null;
477        }
478
479        if (!$request_val = $parser->getResponse()) {
480            return null;
481        }
482        if (!is_a($request_val, 'SOAP_Value')) {
483            $this->_raiseSoapFault('Parser did not return SOAP_Value object: ' . $request_val, '', '', 'Server');
484            return null;
485        }
486
487        /* Verify that SOAP_Value objects in request match the methods
488         * signature. */
489        if (!$this->verifyMethod($request_val)) {
490            /* verifyMethod() creates the fault. */
491            return null;
492        }
493
494        /* Need to set special error detection inside the value class to
495         * differentiate between no params passed, and an error decoding. */
496        $request_data = $this->__decodeRequest($request_val);
497        if (PEAR::isError($request_data)) {
498            $this->_raiseSoapFault($request_data);
499            return null;
500        }
501        $method_response =& $this->callMethod($this->call_methodname, $request_data);
502        if (PEAR::isError($method_response)) {
503            $this->_raiseSoapFault($method_response);
504            return null;
505        }
506
507        if ($this->_options['parameters'] ||
508            !$method_response ||
509            $this->_options['style'] == 'rpc') {
510            /* Get the method result. */
511            if (is_null($method_response)) {
512                $return_val = null;
513            } else {
514                $return_val = $this->buildResult($method_response, $this->return_type);
515            }
516
517            $qn = new QName($this->methodname . 'Response', $this->method_namespace);
518            $methodValue = new SOAP_Value($qn->fqn(), 'Struct', $return_val);
519        } else {
520            $methodValue =& $method_response;
521        }
522        return $this->makeEnvelope($methodValue, $header_results, $this->response_encoding);
523    }
524
525    function &__decodeRequest($request, $shift = false)
526    {
527        if (!$request) {
528            $decoded = null;
529            return $decoded;
530        }
531
532        /* Check for valid response. */
533        if (PEAR::isError($request)) {
534            $fault = &$this->_raiseSoapFault($request);
535            return $fault;
536        } else if (!is_a($request, 'SOAP_Value')) {
537            $fault = &$this->_raiseSoapFault('Invalid data in server::__decodeRequest');
538            return $fault;
539        }
540
541        /* Decode to native php datatype. */
542        $requestArray = $this->_decode($request);
543        /* Fault? */
544        if (PEAR::isError($requestArray)) {
545            $fault = &$this->_raiseSoapFault($requestArray);
546            return $fault;
547        }
548        if (is_object($requestArray) &&
549            get_class($requestArray) == 'stdClass') {
550            $requestArray = get_object_vars($requestArray);
551        } elseif ($this->_options['style'] == 'document') {
552            $requestArray = array($requestArray);
553        }
554        if (is_array($requestArray)) {
555            if (isset($requestArray['faultcode']) ||
556                isset($requestArray[SOAP_BASE::SOAPENVPrefix().':faultcode'])) {
557                $faultcode = $faultstring = $faultdetail = $faultactor = '';
558                foreach ($requestArray as $k => $v) {
559                    if (stristr($k, 'faultcode')) {
560                        $faultcode = $v;
561                    }
562                    if (stristr($k, 'faultstring')) {
563                        $faultstring = $v;
564                    }
565                    if (stristr($k, 'detail')) {
566                        $faultdetail = $v;
567                    }
568                    if (stristr($k, 'faultactor')) {
569                        $faultactor = $v;
570                    }
571                }
572                $fault = &$this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode);
573                return $fault;
574            }
575            /* Return array of return values. */
576            if ($shift && count($requestArray) == 1) {
577                $decoded = array_shift($requestArray);
578                return $decoded;
579            }
580            return $requestArray;
581        }
582        return $requestArray;
583    }
584
585    function verifyMethod($request)
586    {
587        if (!$this->callValidation) {
588            return true;
589        }
590
591        $params = $request->value;
592
593        /* Get the dispatch map if one exists. */
594        $map = null;
595        if (array_key_exists($this->methodname, $this->dispatch_map)) {
596            $map = $this->dispatch_map[$this->methodname];
597        } elseif (isset($this->soapobject)) {
598            if (method_exists($this->soapobject, '__dispatch')) {
599                $map = $this->soapobject->__dispatch($this->methodname);
600            } elseif (method_exists($this->soapobject, $this->methodname)) {
601                if (isset($this->soapobject->__dispatch_map[$this->methodname])) {
602                    // pdp - 2011-04-29 - declared output will be copied to return_type and use in buildResult
603                    $map = $this->soapobject->__dispatch_map[$this->methodname];
604                } else {
605                    /* No map, all public functions are SOAP functions. */
606                    return true;
607                }
608            }
609        }
610        if (!$map) {
611            $this->_raiseSoapFault('SOAP request specified an unhandled method "' . $this->methodname . '"', '', '', 'Client');
612            return false;
613        }
614
615        /* If we aliased the SOAP method name to a PHP function, change
616         * call_methodname so we do the right thing. */
617        if (array_key_exists('alias', $map) && !empty($map['alias'])) {
618            $this->call_methodname = $map['alias'];
619        }
620
621        /* If there are input parameters required. */
622        if ($map['in']) {
623            $this->input_value = count($map['in']);
624            $this->return_type = false;
625            if (is_array($map['out'])) {
626                $this->return_type = count($map['out']) > 1
627                    ? $map['out']
628                    : array_shift($map['out']);
629            }
630            if (is_array($params)) {
631                /* Validate the number of parameters. */
632                if (count($params) == count($map['in'])) {
633                    /* Make array of param types. */
634                    foreach ($params as $param) {
635                        $p[] = strtolower($param->type);
636                    }
637                    $sig_t = array_values($map['in']);
638                    /* Validate each param's type. */
639                    for ($i = 0; $i < count($p); $i++) {
640                        /* If SOAP types do not match, it's still fine if the
641                         * mapped php types match this allows using plain PHP
642                         * variables to work (i.e. stuff like Decimal would
643                         * fail otherwise). We consider this only error if the
644                         * types exist in our type maps, and they differ. */
645                        if (strcasecmp($sig_t[$i], $p[$i]) != 0 &&
646                            isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]]) &&
647                            strcasecmp($this->_typemap[SOAP_XML_SCHEMA_VERSION][$sig_t[$i]], $this->_typemap[SOAP_XML_SCHEMA_VERSION][$p[$i]]) != 0) {
648
649                            $param = $params[$i];
650                            $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');
651                            return false;
652                        }
653                    }
654                    return true;
655                } else {
656                    /* Wrong number of params. */
657                    $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" required ' . count($map['in']) . ' and request provided ' . count($params), '', '', 'Client');
658                    return false;
659                }
660            } else {
661                /* No params. */
662                $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" requires ' . count($map['in']) . ' parameters, and request provided none.', '', '', 'Client');
663                return false;
664            }
665        }
666
667        /* We'll try it anyway. */
668        return true;
669    }
670
671    function validateMethod($methodname, $namespace = null)
672    {
673        unset($this->soapobject);
674
675        if (!$this->callValidation) {
676            return true;
677        }
678
679        /* No SOAP access to private functions. */
680        if ($methodname[0] == '_') {
681            return false;
682        }
683
684        /* if it's in our function list, ok */
685        if (array_key_exists($methodname, $this->dispatch_map) &&
686            (!$namespace ||
687             !array_key_exists('namespace', $this->dispatch_map[$methodname]) ||
688             $namespace == $this->dispatch_map[$methodname]['namespace'])) {
689            if (array_key_exists('namespace', $this->dispatch_map[$methodname]))
690                $this->method_namespace = $this->dispatch_map[$methodname]['namespace'];
691            return true;
692        }
693
694        /* if it's in an object, it's ok */
695        if (isset($this->dispatch_objects[$namespace])) {
696            $c = count($this->dispatch_objects[$namespace]);
697            for ($i = 0; $i < $c; $i++) {
698                $obj =& $this->dispatch_objects[$namespace][$i];
699                /* If we have a dispatch map, and the function is not in the
700                 * dispatch map, then it is not callable! */
701                if (method_exists($obj, '__dispatch')) {
702                    if ($obj->__dispatch($methodname)) {
703                        $this->method_namespace = $namespace;
704                        $this->soapobject =& $obj;
705                        return true;
706                    }
707                } elseif (method_exists($obj, $methodname)) {
708                    $this->method_namespace = $namespace;
709                    $this->soapobject =& $obj;
710                    return true;
711                }
712            }
713        }
714
715        return false;
716    }
717
718    function addObjectMap(&$obj, $namespace = null, $service_name = 'Default',
719                          $service_desc = '')
720    {
721        if (!$namespace) {
722            if (isset($obj->namespace)) {
723                // XXX a bit of backwards compatibility
724                $namespace = $obj->namespace;
725            } else {
726                $this->_raiseSoapFault('No namespace provided for class!', '', '', 'Server');
727                return false;
728            }
729        }
730        if (!isset($this->dispatch_objects[$namespace])) {
731            $this->dispatch_objects[$namespace] = array();
732        }
733        $this->dispatch_objects[$namespace][] =& $obj;
734
735        // Create internal WSDL structures for object
736
737        // XXX Because some internal workings of PEAR::SOAP decide whether to
738        // do certain things by the presence or absence of _wsdl, we should
739        // only create a _wsdl structure if we know we can fill it; if
740        // __dispatch_map or __typedef for the object is missing, we should
741        // avoid creating it. Later, when we are using PHP 5 introspection, we
742        // will be able to make the data for all objects without any extra
743        // information from the developers, and this condition should be
744        // dropped.
745
746        // XXX Known issue: if imported WSDL (bindWSDL) or another WSDL source
747        // is used to add _wsdl structure information, then addObjectWSDL is
748        // used, there is a high possibility of _wsdl data corruption;
749        // therefore you should avoid using __dispatch_map/__typedef
750        // definitions AND other WSDL data sources in the same service. We
751        // exclude classes that don't have __typedefs to allow external WSDL
752        // files to be used with classes with no internal type definitions
753        // (the types are defined in the WSDL file). When addObjectWSDL is
754        // refactored to not cause corruption, this restriction can be
755        // relaxed.
756
757        // In summary, if you add an object with both a dispatch map and type
758        // definitions, then previous WSDL file operation and type definitions
759        // will be overwritten.
760        if (isset($obj->__dispatch_map) && isset($obj->__typedef)) {
761            $this->addObjectWSDL($obj, $namespace, $service_name, $service_desc);
762        }
763
764        return true;
765    }
766
767    /**
768     * Adds a method to the dispatch map.
769     */
770    function addToMap($methodname, $in, $out, $namespace = null, $alias = null)
771    {
772        if (!$this->callHandler && !function_exists($methodname)) {
773            $this->_raiseSoapFault('Error mapping function', '', '', 'Server');
774            return false;
775        }
776
777        $this->dispatch_map[$methodname]['in'] = $in;
778        $this->dispatch_map[$methodname]['out'] = $out;
779        $this->dispatch_map[$methodname]['alias'] = $alias;
780        if ($namespace) {
781            $this->dispatch_map[$methodname]['namespace'] = $namespace;
782        }
783
784        return true;
785    }
786
787    function setCallHandler($callHandler, $validation = true)
788    {
789        $this->callHandler = $callHandler;
790        $this->callValidation = $validation;
791    }
792
793    /**
794     * @deprecated use bindWSDL from now on
795     */
796    function bind($wsdl_url)
797    {
798        $this->bindWSDL($wsdl_url);
799    }
800
801    /**
802     * @param  string a url to a WSDL resource
803     * @return void
804     */
805    function bindWSDL($wsdl_url)
806    {
807        /* Instantiate WSDL class. */
808        $this->_wsdl = new SOAP_WSDL($wsdl_url);
809        if ($this->_wsdl->fault) {
810            $this->_raiseSoapFault($this->_wsdl->fault);
811        }
812    }
813
814    /**
815     * @return void
816     */
817    function addObjectWSDL($wsdl_obj, $targetNamespace, $service_name,
818                           $service_desc = '')
819    {
820        if (!isset($this->_wsdl)) {
821            $this->_wsdl = new SOAP_WSDL;
822        }
823
824        $this->_wsdl->parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc);
825
826        if ($this->_wsdl->fault) {
827            $this->_raiseSoapFault($this->_wsdl->fault);
828        }
829    }
830
831}
Note: See TracBrowser for help on using the repository browser.