Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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