~ubuntu-branches/ubuntu/saucy/php-soap/saucy

« back to all changes in this revision

Viewing changes to SOAP-0.13.0/SOAP/WSDL.php

  • Committer: Package Import Robot
  • Author(s): Prach Pongpanich
  • Date: 2013-05-08 15:21:07 UTC
  • mfrom: (1.1.5)
  • Revision ID: package-import@ubuntu.com-20130508152107-x6a6delp9dy112zi
Tags: 0.13.0-1
* New upstream release
* Now using PKG-PHP-PEAR team as maintainer
* Add myself as uploader
* Add debian/gbp.conf file
* Add Vcs-* fields
* Switch to pkg-php-tools and rewrite debian/rules
* Drop debian/docs, upstream don't ship AUTHORS file
* Update copyright file to version 1.0 format
* Update description in debian/control
* Bump compat level to 9
* Bump Standards-Version 3.9.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * This file contains the code for dealing with WSDL access and services.
 
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 'HTTP/Request.php';
 
28
 
 
29
define('WSDL_CACHE_MAX_AGE', 43200);
 
30
 
 
31
/**
 
32
 * This class parses WSDL files, and can be used by SOAP::Client to properly
 
33
 * register soap values for services.
 
34
 *
 
35
 * Originally based on SOAPx4 by Dietrich Ayala
 
36
 * http://dietrich.ganx4.com/soapx4
 
37
 *
 
38
 * @todo
 
39
 * - refactor namespace handling ($namespace/$ns)
 
40
 * - implement IDL type syntax declaration so we can generate WSDL
 
41
 *
 
42
 * @access public
 
43
 * @package SOAP
 
44
 * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
 
45
 * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
 
46
 */
 
47
class SOAP_WSDL extends SOAP_Base
 
48
{
 
49
    var $tns = null;
 
50
    var $definition = array();
 
51
    var $namespaces = array();
 
52
    var $ns = array();
 
53
    var $xsd = SOAP_XML_SCHEMA_VERSION;
 
54
    var $complexTypes = array();
 
55
    var $elements = array();
 
56
    var $messages = array();
 
57
    var $portTypes = array();
 
58
    var $bindings = array();
 
59
    var $imports = array();
 
60
    var $services = array();
 
61
    var $service = '';
 
62
 
 
63
    /**
 
64
     * URL to WSDL file.
 
65
     *
 
66
     * @var string
 
67
     */
 
68
    var $uri;
 
69
 
 
70
    /**
 
71
     * Parse documentation in the WSDL?
 
72
     *
 
73
     * @var boolean
 
74
     */
 
75
    var $docs;
 
76
 
 
77
    /**
 
78
     * Proxy parameters.
 
79
     *
 
80
     * @var array
 
81
     */
 
82
    var $proxy;
 
83
 
 
84
    /**
 
85
     * Enable tracing in the generated proxy class?
 
86
     *
 
87
     * @var boolean
 
88
     */
 
89
    var $trace = false;
 
90
 
 
91
    /**
 
92
     * Use WSDL cache?
 
93
     *
 
94
     * @var boolean
 
95
     */
 
96
    var $cacheUse;
 
97
 
 
98
    /**
 
99
     * WSDL cache directory.
 
100
     *
 
101
     * @var string
 
102
     */
 
103
    var $cacheDir;
 
104
 
 
105
    /**
 
106
     * Cache maximum lifetime (in seconds).
 
107
     *
 
108
     * @var integer
 
109
     */
 
110
    var $cacheMaxAge;
 
111
 
 
112
    /**
 
113
     * Class to use for WSDL parsing. Can be overridden for special cases,
 
114
     * subclasses, etc.
 
115
     *
 
116
     * @var string
 
117
     */
 
118
    var $wsdlParserClass = 'SOAP_WSDL_Parser';
 
119
 
 
120
    /**
 
121
     * Reserved PHP keywords.
 
122
     *
 
123
     * @link http://www.php.net/manual/en/reserved.php
 
124
     *
 
125
     * @var array
 
126
     */
 
127
    var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case',
 
128
                           'catch', 'cfunction', 'class', 'clone', 'const',
 
129
                           'continue', 'declare', 'default', 'die', 'do',
 
130
                           'echo', 'else', 'elseif', 'empty', 'enddeclare',
 
131
                           'endfor', 'endforeach', 'endif', 'endswitch',
 
132
                           'endwhile', 'eval', 'exception', 'exit', 'extends',
 
133
                           'final', 'for', 'foreach', 'function', 'global',
 
134
                           'if', 'implements', 'include', 'include_once',
 
135
                           'interface', 'isset', 'list', 'new', 'old_function',
 
136
                           'or', 'php_user_filter', 'print', 'private',
 
137
                           'protected', 'public', 'require', 'require_once',
 
138
                           'return', 'static', 'switch', 'this', 'throw',
 
139
                           'try', 'unset', 'use', 'var', 'while', 'xor');
 
140
 
 
141
    /**
 
142
     * Regular expressions for invalid PHP labels.
 
143
     *
 
144
     * @link http://www.php.net/manual/en/language.variables.php.
 
145
     *
 
146
     * @var string
 
147
     */
 
148
    var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/');
 
149
 
 
150
    /**
 
151
     * SOAP_WSDL constructor.
 
152
     *
 
153
     * @param string $wsdl_uri          URL to WSDL file.
 
154
     * @param array $proxy              Options for HTTP_Request class
 
155
     *                                  @see HTTP_Request.
 
156
     * @param boolean|string $cacheUse  Use WSDL caching? The cache directory
 
157
     *                                  if a string.
 
158
     * @param integer $cacheMaxAge      Cache maximum lifetime (in seconds).
 
159
     * @param boolean $docs             Parse documentation in the WSDL?
 
160
     *
 
161
     * @access public
 
162
     */
 
163
    function SOAP_WSDL($wsdl_uri    = false,
 
164
                       $proxy       = array(),
 
165
                       $cacheUse    = false,
 
166
                       $cacheMaxAge = WSDL_CACHE_MAX_AGE,
 
167
                       $docs        = false)
 
168
    {
 
169
        parent::SOAP_Base('WSDL');
 
170
        $this->uri         = $wsdl_uri;
 
171
        $this->proxy       = $proxy;
 
172
        $this->cacheUse    = !empty($cacheUse);
 
173
        $this->cacheMaxAge = $cacheMaxAge;
 
174
        $this->docs        = $docs;
 
175
        if (is_string($cacheUse)) {
 
176
            $this->cacheDir = $cacheUse;
 
177
        }
 
178
 
 
179
        if ($wsdl_uri) {
 
180
            if (!PEAR::isError($this->parseURL($wsdl_uri))) {
 
181
                reset($this->services);
 
182
                $this->service = key($this->services);
 
183
            }
 
184
        }
 
185
    }
 
186
 
 
187
    /**
 
188
     * @deprecated  Use setService().
 
189
     */
 
190
    function set_service($service)
 
191
    {
 
192
        $this->setService($service);
 
193
    }
 
194
 
 
195
    /**
 
196
     * Sets the service currently to be used.
 
197
     *
 
198
     * @param string $service  An (existing) service name.
 
199
     */
 
200
    function setService($service)
 
201
    {
 
202
        if (array_key_exists($service, $this->services)) {
 
203
            $this->service = $service;
 
204
        }
 
205
    }
 
206
 
 
207
    /**
 
208
     * Fills the WSDL array tree with data from a WSDL file.
 
209
     *
 
210
     * @param string $wsdl_uri  URL to WSDL file.
 
211
     */
 
212
    function parseURL($wsdl_uri)
 
213
    {
 
214
        $parser = new $this->wsdlParserClass($wsdl_uri, $this, $this->docs);
 
215
 
 
216
        if ($parser->fault) {
 
217
            $this->_raiseSoapFault($parser->fault);
 
218
        }
 
219
    }
 
220
 
 
221
    /**
 
222
     * Fills the WSDL array tree with data from one or more PHP class objects.
 
223
     *
 
224
     * @param mixed $wsdl_obj          An object or array of objects to add to
 
225
     *                                 the internal WSDL tree.
 
226
     * @param string $targetNamespace  The target namespace of schema types
 
227
     *                                 etc.
 
228
     * @param string $service_name     Name of the WSDL service.
 
229
     * @param string $service_desc     Optional description of the WSDL
 
230
     *                                 service.
 
231
     */
 
232
    function parseObject($wsdl_obj, $targetNamespace, $service_name,
 
233
                         $service_desc = '')
 
234
    {
 
235
        $parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this,
 
236
                                             $targetNamespace, $service_name,
 
237
                                             $service_desc);
 
238
 
 
239
        if ($parser->fault) {
 
240
            $this->_raiseSoapFault($parser->fault);
 
241
        }
 
242
    }
 
243
 
 
244
    function getEndpoint($portName)
 
245
    {
 
246
        if ($this->_isfault()) {
 
247
            return $this->_getfault();
 
248
        }
 
249
 
 
250
        return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
 
251
                ? $this->services[$this->service]['ports'][$portName]['address']['location']
 
252
                : $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri);
 
253
    }
 
254
 
 
255
    function _getPortName($operation, $service)
 
256
    {
 
257
        if (isset($this->services[$service]['ports'])) {
 
258
            $ports = $this->services[$service]['ports'];
 
259
            foreach ($ports as $port => $portAttrs) {
 
260
                $type = $ports[$port]['type'];
 
261
                if ($type == 'soap' &&
 
262
                    isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
 
263
                    return $port;
 
264
                }
 
265
            }
 
266
        }
 
267
        return null;
 
268
    }
 
269
 
 
270
    /**
 
271
     * Finds the name of the first port that contains an operation of name
 
272
     * $operation. Always returns a SOAP portName.
 
273
     */
 
274
    function getPortName($operation, $service = null)
 
275
    {
 
276
        if ($this->_isfault()) {
 
277
            return $this->_getfault();
 
278
        }
 
279
 
 
280
        if (!$service) {
 
281
            $service = $this->service;
 
282
        }
 
283
        if (isset($this->services[$service]['ports'])) {
 
284
            if ($portName = $this->_getPortName($operation, $service)) {
 
285
                return $portName;
 
286
            }
 
287
        }
 
288
        // Try any service in the WSDL.
 
289
        foreach ($this->services as $serviceName => $service) {
 
290
            if (isset($this->services[$serviceName]['ports'])) {
 
291
                if ($portName = $this->_getPortName($operation, $serviceName)) {
 
292
                    $this->service = $serviceName;
 
293
                    return $portName;
 
294
                }
 
295
            }
 
296
        }
 
297
        return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri);
 
298
    }
 
299
 
 
300
    function getOperationData($portName, $operation)
 
301
    {
 
302
        if ($this->_isfault()) {
 
303
            return $this->_getfault();
 
304
        }
 
305
 
 
306
        if (!isset($this->services[$this->service]['ports'][$portName]['binding']) ||
 
307
            !($binding = $this->services[$this->service]['ports'][$portName]['binding'])) {
 
308
            return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri);
 
309
        }
 
310
 
 
311
        // Get operation data from binding.
 
312
        if (is_array($this->bindings[$binding]['operations'][$operation])) {
 
313
            $opData = $this->bindings[$binding]['operations'][$operation];
 
314
        }
 
315
        // Get operation data from porttype.
 
316
        $portType = $this->bindings[$binding]['type'];
 
317
        if (!$portType) {
 
318
            return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri);
 
319
        }
 
320
        if (is_array($type = $this->portTypes[$portType][$operation])) {
 
321
            if (isset($type['parameterOrder'])) {
 
322
                $opData['parameterOrder'] = $type['parameterOrder'];
 
323
            }
 
324
            $opData['input'] = array_merge($opData['input'], $type['input']);
 
325
            $opData['output'] = array_merge($opData['output'], $type['output']);
 
326
        }
 
327
        if (!$opData)
 
328
            return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri);
 
329
        $opData['parameters'] = false;
 
330
        if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
 
331
            $opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace'];
 
332
        // Message data from messages.
 
333
        $inputMsg = $opData['input']['message'];
 
334
        if (isset($opData['input']['parts']) &&
 
335
            !is_array($opData['input']['parts'])) {
 
336
            $opData['input']['parts'] = array($opData['input']['parts'] => '');
 
337
 
 
338
        }
 
339
        if (is_array($this->messages[$inputMsg])) {
 
340
            foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
 
341
                if ($opData['style'] == 'document' &&
 
342
                    $opData['input']['use'] == 'literal' &&
 
343
                    $pname == 'parameters') {
 
344
                    $opData['parameters'] = true;
 
345
                    $opData['namespace'] = $this->namespaces[$pattrs['namespace']];
 
346
                    $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
 
347
                    if (isset($el['elements'])) {
 
348
                        foreach ($el['elements'] as $elname => $elattrs) {
 
349
                            $opData['input']['parts'][$elname] = $elattrs;
 
350
                        }
 
351
                    }
 
352
                } else {
 
353
                    $opData['input']['parts'][$pname] = $pattrs;
 
354
                }
 
355
            }
 
356
        }
 
357
        $outputMsg = $opData['output']['message'];
 
358
        if (isset($opData['output']['parts']) &&
 
359
            !is_array($opData['output']['parts'])) {
 
360
            $opData['output']['parts'] = array($opData['output']['parts'] => '');
 
361
        }
 
362
        if (is_array($this->messages[$outputMsg])) {
 
363
            foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
 
364
                if ($opData['style'] == 'document' &&
 
365
                    $opData['output']['use'] == 'literal' &&
 
366
                    $pname == 'parameters') {
 
367
 
 
368
                    $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
 
369
                    if (isset($el['elements'])) {
 
370
                        foreach ($el['elements'] as $elname => $elattrs) {
 
371
                            $opData['output']['parts'][$elname] = $elattrs;
 
372
                        }
 
373
                    }
 
374
                } else {
 
375
                    $opData['output']['parts'][$pname] = $pattrs;
 
376
                }
 
377
            }
 
378
        }
 
379
        return $opData;
 
380
    }
 
381
 
 
382
    function matchMethod(&$operation)
 
383
    {
 
384
        if ($this->_isfault()) {
 
385
            return $this->_getfault();
 
386
        }
 
387
 
 
388
        // Overloading lowercases function names :(
 
389
        foreach ($this->services[$this->service]['ports'] as $portAttrs) {
 
390
            foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) {
 
391
                if (strcasecmp($op, $operation) == 0) {
 
392
                    $operation = $op;
 
393
                }
 
394
            }
 
395
        }
 
396
    }
 
397
 
 
398
    /**
 
399
     * Given a datatype, what function handles the processing?
 
400
     *
 
401
     * This is used for doc/literal requests where we receive a datatype, and
 
402
     * we need to pass it to a method in out server class.
 
403
     *
 
404
     * @param string $datatype
 
405
     * @param string $namespace
 
406
     * @return string
 
407
     * @access public
 
408
     */
 
409
    function getDataHandler($datatype, $namespace)
 
410
    {
 
411
        // See if we have an element by this name.
 
412
        if (isset($this->namespaces[$namespace])) {
 
413
            $namespace = $this->namespaces[$namespace];
 
414
        }
 
415
 
 
416
        if (!isset($this->ns[$namespace])) {
 
417
            return null;
 
418
        }
 
419
 
 
420
        $nsp = $this->ns[$namespace];
 
421
        //if (!isset($this->elements[$nsp]))
 
422
        //    $nsp = $this->namespaces[$nsp];
 
423
        if (!isset($this->elements[$nsp][$datatype])) {
 
424
            return null;
 
425
        }
 
426
 
 
427
        $checkmessages = array();
 
428
        // Find what messages use this datatype.
 
429
        foreach ($this->messages as $messagename => $message) {
 
430
            foreach ($message as $part) {
 
431
                if ($part['type'] == $datatype) {
 
432
                    $checkmessages[] = $messagename;
 
433
                    break;
 
434
                }
 
435
            }
 
436
        }
 
437
        // Find the operation that uses this message.
 
438
        foreach($this->portTypes as $porttype) {
 
439
            foreach ($porttype as $opname => $opinfo) {
 
440
                foreach ($checkmessages as $messagename) {
 
441
                    if ($opinfo['input']['message'] == $messagename) {
 
442
                        return $opname;
 
443
                    }
 
444
                }
 
445
            }
 
446
        }
 
447
 
 
448
        return null;
 
449
    }
 
450
 
 
451
    function getSoapAction($portName, $operation)
 
452
    {
 
453
        if ($this->_isfault()) {
 
454
            return $this->_getfault();
 
455
        }
 
456
 
 
457
        if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
 
458
            return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
 
459
        }
 
460
 
 
461
        return false;
 
462
    }
 
463
 
 
464
    function getNamespace($portName, $operation)
 
465
    {
 
466
        if ($this->_isfault()) {
 
467
            return $this->_getfault();
 
468
        }
 
469
 
 
470
        if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
 
471
            return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
 
472
        }
 
473
 
 
474
        return false;
 
475
    }
 
476
 
 
477
    function getNamespaceAttributeName($namespace)
 
478
    {
 
479
        /* If it doesn't exist at first, flip the array and check again. */
 
480
        if (empty($this->ns[$namespace])) {
 
481
            $this->ns = array_flip($this->namespaces);
 
482
        }
 
483
 
 
484
        /* If it doesn't exist now, add it. */
 
485
        if (empty($this->ns[$namespace])) {
 
486
            return $this->addNamespace($namespace);
 
487
        }
 
488
 
 
489
        return $this->ns[$namespace];
 
490
    }
 
491
 
 
492
    function addNamespace($namespace)
 
493
    {
 
494
        if (!empty($this->ns[$namespace])) {
 
495
            return $this->ns[$namespace];
 
496
        }
 
497
 
 
498
        $n = count($this->ns);
 
499
        $attr = 'ns' . $n;
 
500
        $this->namespaces['ns' . $n] = $namespace;
 
501
        $this->ns[$namespace] = $attr;
 
502
 
 
503
        return $attr;
 
504
    }
 
505
 
 
506
    function _validateString($string)
 
507
    {
 
508
        return preg_match('/^[\w_:#\/]+$/', $string);
 
509
    }
 
510
 
 
511
    function _addArg(&$args, &$argarray, $argname)
 
512
    {
 
513
        if ($args) {
 
514
            $args .= ', ';
 
515
        }
 
516
        $args .= '$' . $argname;
 
517
        if (!$this->_validateString($argname)) {
 
518
            return;
 
519
        }
 
520
        if ($argarray) {
 
521
            $argarray .= ', ';
 
522
        }
 
523
        $argarray .= "'$argname' => $" . $argname;
 
524
    }
 
525
 
 
526
    function _elementArg(&$args, &$argarray, &$_argtype, $_argname)
 
527
    {
 
528
        $comments = '';
 
529
        $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
 
530
        $tns = isset($this->ns[$el['namespace']])
 
531
            ? $this->ns[$el['namespace']]
 
532
            : $_argtype['namespace'];
 
533
 
 
534
        if (!empty($el['complex']) ||
 
535
            (isset($el['type']) &&
 
536
             isset($this->complexTypes[$tns][$el['type']]))) {
 
537
            // The element is a complex type.
 
538
            $comments .= "        // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
 
539
            $attrname = "{$_argtype['type']}_attr";
 
540
            if (isset($el['type']) &&
 
541
                isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
 
542
                $comments .= "        // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
 
543
            }
 
544
            $comments .= "        \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
 
545
            $comments .= "        \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
 
546
            $this->_addArg($args, $argarray, $_argtype['type']);
 
547
            if (isset($el['type']) &&
 
548
                isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
 
549
                if ($args) {
 
550
                    $args .= ', ';
 
551
                }
 
552
                $args .= '$' . $attrname;
 
553
            }
 
554
        } elseif (isset($el['elements'])) {
 
555
            foreach ($el['elements'] as $ename => $element) {
 
556
                $comments .= "        \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" .
 
557
                    (isset($element['type']) ? $element['type'] : false) .
 
558
                    "', \$$ename);\n";
 
559
                $this->_addArg($args, $argarray, $ename);
 
560
            }
 
561
        } else {
 
562
            $comments .= "        \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n";
 
563
            $this->_addArg($args, $argarray, $_argname);
 
564
        }
 
565
 
 
566
        return $comments;
 
567
    }
 
568
 
 
569
    function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname)
 
570
    {
 
571
        $comments = '';
 
572
        if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
 
573
            $comments  = "        // $_argname is a ComplexType {$_argtype['type']},\n" .
 
574
                "        // refer to wsdl for more info\n";
 
575
            if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
 
576
                $comments .= "        // $_argname may require attributes, refer to wsdl for more info\n";
 
577
            }
 
578
            $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type'];
 
579
            $comments .= "        \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
 
580
        }
 
581
 
 
582
        $this->_addArg($args, $argarray, $_argname);
 
583
 
 
584
        return $comments;
 
585
    }
 
586
 
 
587
    /**
 
588
     * Generates stub code from the WSDL that can be saved to a file or eval'd
 
589
     * into existence.
 
590
     */
 
591
    function generateProxyCode($port = '', $classname = '')
 
592
    {
 
593
        if ($this->_isfault()) {
 
594
            return $this->_getfault();
 
595
        }
 
596
 
 
597
        $multiport = count($this->services[$this->service]['ports']) > 1;
 
598
        if (!$port) {
 
599
            reset($this->services[$this->service]['ports']);
 
600
            $port = current($this->services[$this->service]['ports']);
 
601
        }
 
602
        // XXX currently do not support HTTP ports
 
603
        if ($port['type'] != 'soap') {
 
604
            return null;
 
605
        }
 
606
 
 
607
        // XXX currentPort is BAD
 
608
        $clienturl = $port['address']['location'];
 
609
        if (!$classname) {
 
610
            if ($multiport || $port) {
 
611
                $classname = 'WebService_' . $this->service . '_' . $port['name'];
 
612
            } else {
 
613
                $classname = 'WebService_' . $this->service;
 
614
            }
 
615
            $classname = $this->_sanitize($classname);
 
616
        }
 
617
 
 
618
        if (!$this->_validateString($classname)) {
 
619
            return null;
 
620
        }
 
621
 
 
622
        if (is_array($this->proxy) && count($this->proxy)) {
 
623
            $class = "class $classname extends SOAP_Client\n{\n" .
 
624
            "    function $classname(\$path = '$clienturl')\n    {\n" .
 
625
            "        \$this->SOAP_Client(\$path, 0, 0,\n" .
 
626
            '                           array(';
 
627
 
 
628
            foreach ($this->proxy as $key => $val) {
 
629
                if (is_array($val)) {
 
630
                    $class .= "'$key' => array(";
 
631
                    foreach ($val as $key2 => $val2) {
 
632
                        $class .= "'$key2' => '$val2', ";
 
633
                    }
 
634
                    $class .= ')';
 
635
                } else {
 
636
                    $class .= "'$key' => '$val', ";
 
637
                }
 
638
            }
 
639
            $class .= "));\n    }\n";
 
640
            $class = str_replace(', ))', '))', $class);
 
641
        } else {
 
642
            $class = "class $classname extends SOAP_Client\n{\n" .
 
643
            "    function $classname(\$path = '$clienturl')\n    {\n" .
 
644
            "        \$this->SOAP_Client(\$path, 0);\n" .
 
645
            "    }\n";
 
646
        }
 
647
 
 
648
        // Get the binding, from that get the port type.
 
649
        $primaryBinding = $port['binding'];
 
650
        $primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding);
 
651
        $portType = $this->bindings[$primaryBinding]['type'];
 
652
        $portType = preg_replace("/^(.*:)/", '', $portType);
 
653
        $style = $this->bindings[$primaryBinding]['style'];
 
654
 
 
655
        // XXX currentPortType is BAD
 
656
        foreach ($this->portTypes[$portType] as $opname => $operation) {
 
657
            $binding = $this->bindings[$primaryBinding]['operations'][$opname];
 
658
            if (isset($binding['soapAction'])) {
 
659
                $soapaction = $binding['soapAction'];
 
660
            } else {
 
661
                $soapaction = null;
 
662
            }
 
663
            if (isset($binding['style'])) {
 
664
                $opstyle = $binding['style'];
 
665
            } else {
 
666
                $opstyle = $style;
 
667
            }
 
668
            $use = $binding['input']['use'];
 
669
            if ($use == 'encoded') {
 
670
                $namespace = $binding['input']['namespace'];
 
671
            } else {
 
672
                $bindingType = $this->bindings[$primaryBinding]['type'];
 
673
                $ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
 
674
                $namespace = $this->namespaces[$ns];
 
675
            }
 
676
 
 
677
            $args = '';
 
678
            $argarray = '';
 
679
            $comments = '';
 
680
            $wrappers = '';
 
681
            foreach ($operation['input'] as $argname => $argtype) {
 
682
                if ($argname == 'message') {
 
683
                    foreach ($this->messages[$argtype] as $_argname => $_argtype) {
 
684
                        $_argname = $this->_sanitize($_argname);
 
685
                        if ($opstyle == 'document' && $use == 'literal' &&
 
686
                            $_argtype['name'] == 'parameters') {
 
687
                            // The type or element refered to is used for
 
688
                            // parameters.
 
689
                            $elattrs = null;
 
690
                            $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
 
691
 
 
692
                            if ($el['complex']) {
 
693
                                $namespace = $this->namespaces[$_argtype['namespace']];
 
694
                                // XXX need to wrap the parameters in a
 
695
                                // SOAP_Value.
 
696
                            }
 
697
                            if (isset($el['elements'])) {
 
698
                                foreach ($el['elements'] as $elname => $elattrs) {
 
699
                                    $elname = $this->_sanitize($elname);
 
700
                                    // Is the element a complex type?
 
701
                                    if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
 
702
                                        $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
 
703
                                    } else {
 
704
                                        $this->_addArg($args, $argarray, $elname);
 
705
                                    }
 
706
                                }
 
707
                            }
 
708
                            if ($el['complex'] && $argarray) {
 
709
                                $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name'];
 
710
                                $comments .= "        \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
 
711
                                $argarray = "'{$el['name']}' => \${$el['name']}";
 
712
                            }
 
713
                        } else {
 
714
                            if (isset($_argtype['element'])) {
 
715
                                // Element argument.
 
716
                                $comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']);
 
717
                            } else {
 
718
                                // Complex type argument.
 
719
                                $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
 
720
                            }
 
721
                        }
 
722
                    }
 
723
                }
 
724
            }
 
725
 
 
726
            // Validate entries.
 
727
 
 
728
            // Operation names are function names, so try to make sure it's
 
729
            // legal. This could potentially cause collisions, but let's try
 
730
            // to make everything callable and see how many problems that
 
731
            // causes.
 
732
            $opname_php = $this->_sanitize($opname);
 
733
            if (!$this->_validateString($opname_php)) {
 
734
                return null;
 
735
            }
 
736
 
 
737
            if ($argarray) {
 
738
                $argarray = "array($argarray)";
 
739
            } else {
 
740
                $argarray = 'null';
 
741
            }
 
742
 
 
743
            $class .= "    function &$opname_php($args)\n    {\n$comments$wrappers" .
 
744
                "        \$result = \$this->call('$opname',\n" .
 
745
                "                              \$v = $argarray,\n" .
 
746
                "                              array('namespace' => '$namespace',\n" .
 
747
                "                                    'soapaction' => '$soapaction',\n" .
 
748
                "                                    'style' => '$opstyle',\n" .
 
749
                "                                    'use' => '$use'" .
 
750
                ($this->trace ? ",\n                                    'trace' => true" : '') . "));\n" .
 
751
                "        return \$result;\n" .
 
752
                "    }\n";
 
753
        }
 
754
 
 
755
        $class .= "}\n";
 
756
 
 
757
        return $class;
 
758
    }
 
759
 
 
760
    function generateAllProxies()
 
761
    {
 
762
        $proxycode = '';
 
763
        foreach (array_keys($this->services[$this->service]['ports']) as $key) {
 
764
            $port =& $this->services[$this->service]['ports'][$key];
 
765
            $proxycode .= $this->generateProxyCode($port);
 
766
        }
 
767
        return $proxycode;
 
768
    }
 
769
 
 
770
    function &getProxy($port = '', $name = '')
 
771
    {
 
772
        if ($this->_isfault()) {
 
773
            $fault =& $this->_getfault();
 
774
            return $fault;
 
775
        }
 
776
 
 
777
        $multiport = count($this->services[$this->service]['ports']) > 1;
 
778
 
 
779
        if (!$port) {
 
780
            reset($this->services[$this->service]['ports']);
 
781
            $port = current($this->services[$this->service]['ports']);
 
782
        }
 
783
 
 
784
        if ($multiport || $port) {
 
785
            $classname = 'WebService_' . $this->service . '_' . $port['name'];
 
786
        } else {
 
787
            $classname = 'WebService_' . $this->service;
 
788
        }
 
789
 
 
790
        if ($name) {
 
791
            $classname = $name . '_' . $classname;
 
792
        }
 
793
 
 
794
        $classname = $this->_sanitize($classname);
 
795
        if (!class_exists($classname)) {
 
796
            $proxy = $this->generateProxyCode($port, $classname);
 
797
            require_once 'SOAP/Client.php';
 
798
            eval($proxy);
 
799
        }
 
800
        $proxy = new $classname;
 
801
 
 
802
        return $proxy;
 
803
    }
 
804
 
 
805
    /**
 
806
     * Sanitizes a SOAP value, method or class name so that it can be used as
 
807
     * a valid PHP identifier. Invalid characters are converted into
 
808
     * underscores and reserved words are prefixed with an underscore.
 
809
     *
 
810
     * @param string $name  The identifier to sanitize.
 
811
     *
 
812
     * @return string  The sanitized identifier.
 
813
     */
 
814
    function _sanitize($name)
 
815
    {
 
816
        $name = preg_replace($this->_invalid, '_', $name);
 
817
        if (in_array($name, $this->_reserved)) {
 
818
            $name = '_' . $name;
 
819
        }
 
820
        return $name;
 
821
    }
 
822
 
 
823
    function &_getComplexTypeForElement($name, $namespace)
 
824
    {
 
825
        $t = null;
 
826
        if (isset($this->ns[$namespace]) &&
 
827
            isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
 
828
 
 
829
            $type = $this->elements[$this->ns[$namespace]][$name]['type'];
 
830
            $ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
 
831
 
 
832
            if (isset($this->complexTypes[$ns][$type])) {
 
833
                $t = $this->complexTypes[$ns][$type];
 
834
            }
 
835
        }
 
836
        return $t;
 
837
    }
 
838
 
 
839
    function getComplexTypeNameForElement($name, $namespace)
 
840
    {
 
841
        $t = $this->_getComplexTypeForElement($name, $namespace);
 
842
        if ($t) {
 
843
            return $t['name'];
 
844
        }
 
845
        return null;
 
846
    }
 
847
 
 
848
    function getComplexTypeChildType($ns, $name, $child_ns, $child_name)
 
849
    {
 
850
        // Is the type an element?
 
851
        $t = $this->_getComplexTypeForElement($name, $ns);
 
852
        if ($t) {
 
853
            // No, get it from complex types directly.
 
854
            if (isset($t['elements'][$child_name]['type']))
 
855
                return $t['elements'][$child_name]['type'];
 
856
        } elseif (isset($this->ns[$ns]) &&
 
857
                  isset($this->elements[$this->ns[$ns]][$name]['complex']) &&
 
858
                  $this->elements[$this->ns[$ns]][$name]['complex']) {
 
859
            // Type is not an element but complex.
 
860
            return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
 
861
        }
 
862
        return null;
 
863
    }
 
864
 
 
865
    /**
 
866
     * @param QName $name  A parameter name.
 
867
     * @param QName $type  A parameter type.
 
868
     *
 
869
     * @return array  A list of [type, array element type, array element
 
870
     *                namespace, array length].
 
871
     */
 
872
    function getSchemaType($type, $name)
 
873
    {
 
874
        // see if it's a complex type so we can deal properly with
 
875
        // SOAPENC:arrayType.
 
876
        if ($name && $type) {
 
877
            // XXX TODO:
 
878
            // look up the name in the wsdl and validate the type.
 
879
            foreach ($this->complexTypes as $types) {
 
880
                if (isset($types[$type->name])) {
 
881
                    if (isset($types[$type->name]['type'])) {
 
882
                        list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type->name]['arrayType'])
 
883
                            ? $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType'])
 
884
                            : array($this->namespaces[$types[$type->name]['namespace']], null, 0);
 
885
                        return array($types[$type->name]['type'], $arraytype, $arraytype_ns, $array_depth);
 
886
                    }
 
887
                    if (isset($types[$type->name]['arrayType'])) {
 
888
                        list($arraytype_ns, $arraytype, $array_depth) =
 
889
                            $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType']);
 
890
                        return array('Array', $arraytype, $arraytype_ns, $array_depth);
 
891
                    }
 
892
                    if (!empty($types[$type->name]['elements'][$name->name])) {
 
893
                        $type->name = $types[$type->name]['elements']['type'];
 
894
                        return array($type->name, null, $this->namespaces[$types[$type->name]['namespace']], null);
 
895
                    }
 
896
                    break;
 
897
                }
 
898
            }
 
899
        }
 
900
        if ($type && $type->namespace) {
 
901
            $arrayType = null;
 
902
            // XXX TODO:
 
903
            // this code currently handles only one way of encoding array
 
904
            // types in wsdl need to do a generalized function to figure out
 
905
            // complex types
 
906
            $p = $this->ns[$type->namespace];
 
907
            if ($p && !empty($this->complexTypes[$p][$type->name])) {
 
908
                if ($arrayType = $this->complexTypes[$p][$type->name]['arrayType']) {
 
909
                    $type->name = 'Array';
 
910
                } elseif ($this->complexTypes[$p][$type->name]['order'] == 'sequence' &&
 
911
                          array_key_exists('elements', $this->complexTypes[$p][$type->name])) {
 
912
                    reset($this->complexTypes[$p][$type->name]['elements']);
 
913
                    // assume an array
 
914
                    if (count($this->complexTypes[$p][$type->name]['elements']) == 1) {
 
915
                        $arg = current($this->complexTypes[$p][$type->name]['elements']);
 
916
                        $arrayType = $arg['type'];
 
917
                        $type->name = 'Array';
 
918
                    } else {
 
919
                        foreach ($this->complexTypes[$p][$type->name]['elements'] as $element) {
 
920
                            if ($element['name'] == $type->name) {
 
921
                                $arrayType = $element['type'];
 
922
                                $type->name = $element['type'];
 
923
                            }
 
924
                        }
 
925
                    }
 
926
                } else {
 
927
                    $type->name = 'Struct';
 
928
                }
 
929
                return array($type->name, $arrayType, $type->namespace, null);
 
930
            }
 
931
        }
 
932
        return array(null, null, null, null);
 
933
    }
 
934
 
 
935
    /**
 
936
     * Recurse through the WSDL structure looking for the innermost array type
 
937
     * of multi-dimensional arrays.
 
938
     *
 
939
     * Takes a namespace prefix and a type, which can be in the form 'type' or
 
940
     * 'type[]', and returns the full namespace URI, the type of the most
 
941
     * deeply nested array type found, and the number of levels of nesting.
 
942
     *
 
943
     * @access private
 
944
     * @return mixed array or nothing
 
945
     */
 
946
    function _getDeepestArrayType($nsPrefix, $arrayType)
 
947
    {
 
948
        static $trail = array();
 
949
 
 
950
        $arrayType = preg_replace('/\[\]$/', '', $arrayType);
 
951
 
 
952
        // Protect against circular references XXX We really need to remove
 
953
        // trail from this altogether (it's very inefficient and in the wrong
 
954
        // place!) and put circular reference checking in when the WSDL info
 
955
        // is generated in the first place
 
956
        if (array_search($nsPrefix . ':' . $arrayType, $trail)) {
 
957
            return array(null, null, -count($trail));
 
958
        }
 
959
 
 
960
        if (array_key_exists($nsPrefix, $this->complexTypes) &&
 
961
            array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) &&
 
962
            array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) {
 
963
            $trail[] = $nsPrefix . ':' . $arrayType;
 
964
            $result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
 
965
                                                  $this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
 
966
            return array($result[0], $result[1], $result[2] + 1);
 
967
        }
 
968
        return array($this->namespaces[$nsPrefix], $arrayType, 0);
 
969
    }
 
970
 
 
971
}
 
972
 
 
973
class SOAP_WSDL_Cache extends SOAP_Base
 
974
{
 
975
    /**
 
976
     * Use WSDL cache?
 
977
     *
 
978
     * @var boolean
 
979
     */
 
980
    var $_cacheUse;
 
981
 
 
982
    /**
 
983
     * WSDL cache directory.
 
984
     *
 
985
     * @var string
 
986
     */
 
987
    var $_cacheDir;
 
988
 
 
989
    /**
 
990
     * Cache maximum lifetime (in seconds)
 
991
     *
 
992
     * @var integer
 
993
     */
 
994
    var $_cacheMaxAge;
 
995
 
 
996
    /**
 
997
     * Constructor.
 
998
     *
 
999
     * @param boolean $cashUse      Use caching?
 
1000
     * @param integer $cacheMaxAge  Cache maximum lifetime (in seconds)
 
1001
     */
 
1002
    function SOAP_WSDL_Cache($cacheUse = false,
 
1003
                             $cacheMaxAge = WSDL_CACHE_MAX_AGE,
 
1004
                             $cacheDir = null)
 
1005
    {
 
1006
        parent::SOAP_Base('WSDLCACHE');
 
1007
        $this->_cacheUse = $cacheUse;
 
1008
        $this->_cacheDir = $cacheDir;
 
1009
        $this->_cacheMaxAge = $cacheMaxAge;
 
1010
    }
 
1011
 
 
1012
    /**
 
1013
     * Returns the path to the cache and creates it, if it doesn't exist.
 
1014
     *
 
1015
     * @private
 
1016
     *
 
1017
     * @return string  The directory to use for the cache.
 
1018
     */
 
1019
    function _cacheDir()
 
1020
    {
 
1021
        if (!empty($this->_cacheDir)) {
 
1022
            $dir = $this->_cacheDir;
 
1023
        } else {
 
1024
            $dir = getenv('WSDLCACHE');
 
1025
            if (empty($dir)) {
 
1026
                $dir = './wsdlcache';
 
1027
            }
 
1028
        }
 
1029
        @mkdir($dir, 0700);
 
1030
        return $dir;
 
1031
    }
 
1032
 
 
1033
    /**
 
1034
     * Retrieves a file from cache if it exists, otherwise retreive from net,
 
1035
     * add to cache, and return from cache.
 
1036
     *
 
1037
     * @param  string   URL to WSDL
 
1038
     * @param  array    proxy parameters
 
1039
     * @param  int      expected MD5 of WSDL URL
 
1040
     * @access public
 
1041
     * @return string  data
 
1042
     */
 
1043
    function get($wsdl_fname, $proxy_params = array(), $cache = 0)
 
1044
    {
 
1045
        $cachename = $md5_wsdl = $file_data = '';
 
1046
        if ($this->_cacheUse) {
 
1047
            // Try to retrieve WSDL from cache
 
1048
            $cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl';
 
1049
            if (file_exists($cachename) &&
 
1050
                $file_data = file_get_contents($cachename)) {
 
1051
                $md5_wsdl = md5($file_data);
 
1052
                if ($cache) {
 
1053
                    if ($cache != $md5_wsdl) {
 
1054
                        return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
 
1055
                    }
 
1056
                } else {
 
1057
                    $fi = stat($cachename);
 
1058
                    $cache_mtime = $fi[8];
 
1059
                    if ($cache_mtime + $this->_cacheMaxAge < time()) {
 
1060
                        // Expired, refetch.
 
1061
                        $md5_wsdl = '';
 
1062
                    }
 
1063
                }
 
1064
            }
 
1065
        }
 
1066
 
 
1067
        // Not cached or not using cache. Retrieve WSDL from URL
 
1068
        if (!$md5_wsdl) {
 
1069
            // Is it a local file?
 
1070
            if (strpos($wsdl_fname, 'file://') === 0) {
 
1071
                $wsdl_fname = substr($wsdl_fname, 7);
 
1072
                if (!file_exists($wsdl_fname)) {
 
1073
                    return $this->_raiseSoapFault('Unable to read local WSDL file', $wsdl_fname);
 
1074
                }
 
1075
                $file_data = file_get_contents($wsdl_fname);
 
1076
            } elseif (!preg_match('|^https?://|', $wsdl_fname)) {
 
1077
                return $this->_raiseSoapFault('Unknown schema of WSDL URL', $wsdl_fname);
 
1078
            } else {
 
1079
                $uri = explode('?', $wsdl_fname);
 
1080
                $rq = new HTTP_Request($uri[0], $proxy_params);
 
1081
                // the user agent HTTP_Request uses fouls things up
 
1082
                if (isset($uri[1])) {
 
1083
                    $rq->addRawQueryString($uri[1]);
 
1084
                }
 
1085
 
 
1086
                if (isset($proxy_params['proxy_host']) &&
 
1087
                    isset($proxy_params['proxy_port']) &&
 
1088
                    isset($proxy_params['proxy_user']) &&
 
1089
                    isset($proxy_params['proxy_pass'])) {
 
1090
                    $rq->setProxy($proxy_params['proxy_host'],
 
1091
                                  $proxy_params['proxy_port'],
 
1092
                                  $proxy_params['proxy_user'],
 
1093
                                  $proxy_params['proxy_pass']);
 
1094
                } elseif (isset($proxy_params['proxy_host']) &&
 
1095
                          isset($proxy_params['proxy_port'])) {
 
1096
                    $rq->setProxy($proxy_params['proxy_host'],
 
1097
                                  $proxy_params['proxy_port']);
 
1098
                }
 
1099
 
 
1100
                $result = $rq->sendRequest();
 
1101
                if (PEAR::isError($result)) {
 
1102
                    return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname);
 
1103
                }
 
1104
                $file_data = $rq->getResponseBody();
 
1105
                if (!$file_data) {
 
1106
                    return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname);
 
1107
                }
 
1108
            }
 
1109
 
 
1110
            $md5_wsdl = md5($file_data);
 
1111
 
 
1112
            if ($this->_cacheUse) {
 
1113
                $fp = fopen($cachename, "wb");
 
1114
                fwrite($fp, $file_data);
 
1115
                fclose($fp);
 
1116
            }
 
1117
        }
 
1118
 
 
1119
        if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
 
1120
            return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
 
1121
        }
 
1122
 
 
1123
        return $file_data;
 
1124
    }
 
1125
 
 
1126
}
 
1127
 
 
1128
class SOAP_WSDL_Parser extends SOAP_Base
 
1129
{
 
1130
 
 
1131
    /**
 
1132
     * Define internal arrays of bindings, ports, operations,
 
1133
     * messages, etc.
 
1134
     */
 
1135
    var $currentMessage;
 
1136
    var $currentOperation;
 
1137
    var $currentPortType;
 
1138
    var $currentBinding;
 
1139
    var $currentPort;
 
1140
 
 
1141
    /**
 
1142
     * Parser vars.
 
1143
     */
 
1144
    var $cache;
 
1145
 
 
1146
    var $tns = null;
 
1147
    var $soapns = array('soap');
 
1148
    var $uri = '';
 
1149
    var $wsdl = null;
 
1150
 
 
1151
    var $status = '';
 
1152
    var $element_stack = array();
 
1153
    var $parentElement = '';
 
1154
 
 
1155
    var $schema = '';
 
1156
    var $schemaStatus = '';
 
1157
    var $schema_stack = array();
 
1158
    var $currentComplexType;
 
1159
    var $schema_element_stack = array();
 
1160
    var $currentElement;
 
1161
 
 
1162
    /**
 
1163
     * Constructor.
 
1164
     */
 
1165
    function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false)
 
1166
    {
 
1167
        parent::SOAP_Base('WSDLPARSER');
 
1168
        $this->cache = new SOAP_WSDL_Cache($wsdl->cacheUse,
 
1169
                                            $wsdl->cacheMaxAge,
 
1170
                                            $wsdl->cacheDir);
 
1171
        $this->uri = $uri;
 
1172
        $this->wsdl = &$wsdl;
 
1173
        $this->docs = $docs;
 
1174
        $this->parse($uri);
 
1175
    }
 
1176
 
 
1177
    function parse($uri)
 
1178
    {
 
1179
        // Check whether content has been read.
 
1180
        $fd = $this->cache->get($uri, $this->wsdl->proxy);
 
1181
        if (PEAR::isError($fd)) {
 
1182
            return $this->_raiseSoapFault($fd);
 
1183
        }
 
1184
 
 
1185
        // Create an XML parser.
 
1186
        $parser = xml_parser_create();
 
1187
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
 
1188
        xml_set_object($parser, $this);
 
1189
        xml_set_element_handler($parser, 'startElement', 'endElement');
 
1190
        if ($this->docs) {
 
1191
            xml_set_character_data_handler($parser, 'characterData');
 
1192
        }
 
1193
 
 
1194
        if (!xml_parse($parser, $fd, true)) {
 
1195
            $detail = sprintf('XML error on line %d: %s',
 
1196
                              xml_get_current_line_number($parser),
 
1197
                              xml_error_string(xml_get_error_code($parser)));
 
1198
            return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
 
1199
        }
 
1200
        xml_parser_free($parser);
 
1201
        return true;
 
1202
    }
 
1203
 
 
1204
    /**
 
1205
     * start-element handler
 
1206
     */
 
1207
    function startElement($parser, $name, $attrs)
 
1208
    {
 
1209
        // Get element prefix.
 
1210
        $qname = new QName($name);
 
1211
        if ($qname->prefix) {
 
1212
            $ns = $qname->prefix;
 
1213
            if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) {
 
1214
                $name = $qname->name;
 
1215
            }
 
1216
        }
 
1217
        $this->currentTag = $qname->name;
 
1218
        $this->parentElement = '';
 
1219
        $stack_size = count($this->element_stack);
 
1220
        if ($stack_size) {
 
1221
            $this->parentElement = $this->element_stack[$stack_size - 1];
 
1222
        }
 
1223
        $this->element_stack[] = $this->currentTag;
 
1224
 
 
1225
        // Find status, register data.
 
1226
        switch ($this->status) {
 
1227
        case 'types':
 
1228
            // sect 2.2 wsdl:types
 
1229
            // children: xsd:schema
 
1230
            $parent_tag = '';
 
1231
            $stack_size = count($this->schema_stack);
 
1232
            if ($stack_size) {
 
1233
                $parent_tag = $this->schema_stack[$stack_size - 1];
 
1234
            }
 
1235
 
 
1236
            switch ($qname->name) {
 
1237
            case 'schema':
 
1238
                // No parent should be in the stack.
 
1239
                if (!$parent_tag || $parent_tag == 'types') {
 
1240
                    if (array_key_exists('targetNamespace', $attrs)) {
 
1241
                        $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
 
1242
                    } else {
 
1243
                        $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
 
1244
                    }
 
1245
                    $this->wsdl->complexTypes[$this->schema] = array();
 
1246
                    $this->wsdl->elements[$this->schema] = array();
 
1247
                }
 
1248
                break;
 
1249
 
 
1250
            case 'complexType':
 
1251
                if ($parent_tag == 'schema') {
 
1252
                    $this->currentComplexType = $attrs['name'];
 
1253
                    if (!isset($attrs['namespace'])) {
 
1254
                        $attrs['namespace'] = $this->schema;
 
1255
                    }
 
1256
                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs;
 
1257
                    if (array_key_exists('base', $attrs)) {
 
1258
                        $qn = new QName($attrs['base']);
 
1259
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
 
1260
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->prefix;
 
1261
                    } else {
 
1262
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
 
1263
                    }
 
1264
                    $this->schemaStatus = 'complexType';
 
1265
                } else {
 
1266
                    $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true;
 
1267
                }
 
1268
                break;
 
1269
 
 
1270
            case 'element':
 
1271
                if (isset($attrs['type'])) {
 
1272
                    $qn = new QName($attrs['type']);
 
1273
                    $attrs['type'] = $qn->name;
 
1274
                    if ($qn->prefix && array_key_exists($qn->prefix, $this->wsdl->namespaces)) {
 
1275
                        $attrs['namespace'] = $qn->prefix;
 
1276
                    }
 
1277
                }
 
1278
 
 
1279
                $parentElement = '';
 
1280
                $stack_size = count($this->schema_element_stack);
 
1281
                if ($stack_size > 0) {
 
1282
                    $parentElement = $this->schema_element_stack[$stack_size - 1];
 
1283
                }
 
1284
 
 
1285
                if (isset($attrs['ref'])) {
 
1286
                    $qn = new QName($attrs['ref']);
 
1287
                    $this->currentElement = $qn->name;
 
1288
                } else {
 
1289
                    $this->currentElement = $attrs['name'];
 
1290
                }
 
1291
                $this->schema_element_stack[] = $this->currentElement;
 
1292
                if (!isset($attrs['namespace'])) {
 
1293
                    $attrs['namespace'] = $this->schema;
 
1294
                }
 
1295
 
 
1296
                if ($parent_tag == 'schema') {
 
1297
                    $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs;
 
1298
                    $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false;
 
1299
                    $this->schemaStatus = 'element';
 
1300
                } elseif ($this->currentComplexType) {
 
1301
                    // we're inside a complexType
 
1302
                    if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) &&
 
1303
                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence')
 
1304
                        && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') {
 
1305
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null;
 
1306
                    }
 
1307
                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs;
 
1308
                } else {
 
1309
                    $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs;
 
1310
                }
 
1311
                break;
 
1312
 
 
1313
            case 'complexContent':
 
1314
            case 'simpleContent':
 
1315
                break;
 
1316
 
 
1317
            case 'extension':
 
1318
            case 'restriction':
 
1319
                if ($this->schemaStatus == 'complexType') {
 
1320
                    if (!empty($attrs['base'])) {
 
1321
                        $qn = new QName($attrs['base']);
 
1322
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
 
1323
 
 
1324
                        // Types that extend from other types aren't
 
1325
                        // *of* those types. Reflect this by denoting
 
1326
                        // which type they extend. I'm leaving the
 
1327
                        // 'type' setting here since I'm not sure what
 
1328
                        // removing it might break at the moment.
 
1329
                        if ($qname->name == 'extension') {
 
1330
                            $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name;
 
1331
                        }
 
1332
                    } else {
 
1333
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
 
1334
                    }
 
1335
                }
 
1336
                break;
 
1337
 
 
1338
            case 'sequence':
 
1339
                if ($this->schemaStatus == 'complexType') {
 
1340
                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
 
1341
                    if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
 
1342
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
 
1343
                    }
 
1344
                }
 
1345
                break;
 
1346
 
 
1347
            case 'all':
 
1348
                $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
 
1349
                if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
 
1350
                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
 
1351
                }
 
1352
                break;
 
1353
 
 
1354
            case 'choice':
 
1355
                $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
 
1356
                if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
 
1357
                    $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
 
1358
                }
 
1359
 
 
1360
            case 'attribute':
 
1361
                if ($this->schemaStatus == 'complexType') {
 
1362
                    if (isset($attrs['name'])) {
 
1363
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs;
 
1364
                    } else {
 
1365
                        if (isset($attrs['ref'])) {
 
1366
                            $q = new QName($attrs['ref']);
 
1367
                            foreach ($attrs as $k => $v) {
 
1368
                                if ($k != 'ref' && strstr($k, $q->name)) {
 
1369
                                    $vq = new QName($v);
 
1370
                                    if ($q->name == 'arrayType') {
 
1371
                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo;
 
1372
                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
 
1373
                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->prefix;
 
1374
                                    } else {
 
1375
                                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name;
 
1376
                                    }
 
1377
                                }
 
1378
                            }
 
1379
                        }
 
1380
                    }
 
1381
                }
 
1382
                break;
 
1383
            }
 
1384
 
 
1385
            $this->schema_stack[] = $qname->name;
 
1386
            break;
 
1387
 
 
1388
        case 'message':
 
1389
            // sect 2.3 wsdl:message child wsdl:part
 
1390
            switch ($qname->name) {
 
1391
            case 'part':
 
1392
                $qn = null;
 
1393
                if (isset($attrs['type'])) {
 
1394
                    $qn = new QName($attrs['type']);
 
1395
                } elseif (isset($attrs['element'])) {
 
1396
                    $qn = new QName($attrs['element']);
 
1397
                }
 
1398
                if ($qn) {
 
1399
                    $attrs['type'] = $qn->name;
 
1400
                    $attrs['namespace'] = $qn->prefix;
 
1401
                }
 
1402
                $this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs;
 
1403
                // error in wsdl
 
1404
 
 
1405
            case 'documentation':
 
1406
                break;
 
1407
 
 
1408
            default:
 
1409
                break;
 
1410
            }
 
1411
            break;
 
1412
 
 
1413
        case 'portType':
 
1414
            // sect 2.4
 
1415
            switch ($qname->name) {
 
1416
            case 'operation':
 
1417
                // attributes: name
 
1418
                // children: wsdl:input wsdl:output wsdl:fault
 
1419
                $this->currentOperation = $attrs['name'];
 
1420
                $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs;
 
1421
                break;
 
1422
 
 
1423
            case 'input':
 
1424
            case 'output':
 
1425
            case 'fault':
 
1426
                // wsdl:input wsdl:output wsdl:fault
 
1427
                // attributes: name message parameterOrder(optional)
 
1428
                if ($this->currentOperation) {
 
1429
                    if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) {
 
1430
                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name], $attrs);
 
1431
                    } else {
 
1432
                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs;
 
1433
                    }
 
1434
                    if (array_key_exists('message', $attrs)) {
 
1435
                        $qn = new QName($attrs['message']);
 
1436
                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name;
 
1437
                        $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->prefix;
 
1438
                    }
 
1439
                }
 
1440
                break;
 
1441
 
 
1442
            case 'documentation':
 
1443
                break;
 
1444
 
 
1445
            default:
 
1446
                break;
 
1447
            }
 
1448
            break;
 
1449
 
 
1450
        case 'binding':
 
1451
            $ns = $qname->prefix ? $this->wsdl->namespaces[$qname->prefix] : SCHEMA_WSDL;
 
1452
            switch ($ns) {
 
1453
            case SCHEMA_SOAP:
 
1454
            case SCHEMA_SOAP12:
 
1455
                // this deals with wsdl section 3 soap binding
 
1456
                switch ($qname->name) {
 
1457
                case 'binding':
 
1458
                    // sect 3.3
 
1459
                    // soap:binding, attributes: transport(required), style(optional, default = document)
 
1460
                    // if style is missing, it is assumed to be 'document'
 
1461
                    if (!isset($attrs['style'])) {
 
1462
                        $attrs['style'] = 'document';
 
1463
                    }
 
1464
                    $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
 
1465
                    break;
 
1466
 
 
1467
                case 'operation':
 
1468
                    // sect 3.4
 
1469
                    // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
 
1470
                    if (!isset($attrs['style'])) {
 
1471
                        $attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style'];
 
1472
                    }
 
1473
                    if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) {
 
1474
                        $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = array_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation], $attrs);
 
1475
                    } else {
 
1476
                        $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs;
 
1477
                    }
 
1478
                    break;
 
1479
 
 
1480
                case 'body':
 
1481
                    // sect 3.5
 
1482
                    // soap:body attributes:
 
1483
                    // part - optional.  listed parts must appear in body, missing means all parts appear in body
 
1484
                    // use - required. encoded|literal
 
1485
                    // encodingStyle - optional.  space seperated list of encodings (uri's)
 
1486
                    $this->wsdl->bindings[$this->currentBinding]
 
1487
                                    ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
 
1488
                    break;
 
1489
 
 
1490
                case 'fault':
 
1491
                    // sect 3.6
 
1492
                    // soap:fault attributes: name use  encodingStyle namespace
 
1493
                    $this->wsdl->bindings[$this->currentBinding]
 
1494
                                    ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
 
1495
                    break;
 
1496
 
 
1497
                case 'header':
 
1498
                    // sect 3.7
 
1499
                    // soap:header attributes: message part use encodingStyle namespace
 
1500
                    $this->wsdl->bindings[$this->currentBinding]
 
1501
                                    ['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
 
1502
                    break;
 
1503
 
 
1504
                case 'headerfault':
 
1505
                    // sect 3.7
 
1506
                    // soap:header attributes: message part use encodingStyle namespace
 
1507
                    $header = count($this->wsdl->bindings[$this->currentBinding]
 
1508
                                    ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1;
 
1509
                    $this->wsdl->bindings[$this->currentBinding]
 
1510
                                    ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs;
 
1511
                    break;
 
1512
 
 
1513
                case 'documentation':
 
1514
                    break;
 
1515
 
 
1516
                default:
 
1517
                    // error!  not a valid element inside binding
 
1518
                    break;
 
1519
                }
 
1520
                break;
 
1521
 
 
1522
            case SCHEMA_WSDL:
 
1523
                // XXX verify correct namespace
 
1524
                // for now, default is the 'wsdl' namespace
 
1525
                // other possible namespaces include smtp, http, etc. for alternate bindings
 
1526
                switch ($qname->name) {
 
1527
                case 'operation':
 
1528
                    // sect 2.5
 
1529
                    // wsdl:operation attributes: name
 
1530
                    $this->currentOperation = $attrs['name'];
 
1531
                    break;
 
1532
 
 
1533
                case 'output':
 
1534
                case 'input':
 
1535
                case 'fault':
 
1536
                    // sect 2.5
 
1537
                    // wsdl:input attributes: name
 
1538
                    $this->opStatus = $qname->name;
 
1539
                    break;
 
1540
 
 
1541
                case 'documentation':
 
1542
                    break;
 
1543
 
 
1544
                default:
 
1545
                    break;
 
1546
                }
 
1547
                break;
 
1548
 
 
1549
            case SCHEMA_WSDL_HTTP:
 
1550
                switch ($qname->name) {
 
1551
                case 'binding':
 
1552
                    // sect 4.4
 
1553
                    // http:binding attributes: verb
 
1554
                    // parent: wsdl:binding
 
1555
                    $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
 
1556
                    break;
 
1557
 
 
1558
                case 'operation':
 
1559
                    // sect 4.5
 
1560
                    // http:operation attributes: location
 
1561
                    // parent: wsdl:operation
 
1562
                    $this->wsdl->bindings[$this->currentBinding]['operations']
 
1563
                                                        [$this->currentOperation] = $attrs;
 
1564
                    break;
 
1565
 
 
1566
                case 'urlEncoded':
 
1567
                    // sect 4.6
 
1568
                    // http:urlEncoded attributes: location
 
1569
                    // parent: wsdl:input wsdl:output etc.
 
1570
                    $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
 
1571
                                                        [$this->currentOperation]['uri'] = 'urlEncoded';
 
1572
                    break;
 
1573
 
 
1574
                case 'urlReplacement':
 
1575
                    // sect 4.7
 
1576
                    // http:urlReplacement attributes: location
 
1577
                    // parent: wsdl:input wsdl:output etc.
 
1578
                    $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
 
1579
                                                        [$this->currentOperation]['uri'] = 'urlReplacement';
 
1580
                    break;
 
1581
 
 
1582
                case 'documentation':
 
1583
                    break;
 
1584
 
 
1585
                default:
 
1586
                    // error
 
1587
                    break;
 
1588
                }
 
1589
 
 
1590
            case SCHEMA_MIME:
 
1591
                // sect 5
 
1592
                // all mime parts are children of wsdl:input, wsdl:output, etc.
 
1593
                // unsuported as of yet
 
1594
                switch ($qname->name) {
 
1595
                case 'content':
 
1596
                    // sect 5.3 mime:content
 
1597
                    // <mime:content part="nmtoken"? type="string"?/>
 
1598
                    // part attribute only required if content is child of multipart related,
 
1599
                    //        it contains the name of the part
 
1600
                    // type attribute contains the mime type
 
1601
                case 'multipartRelated':
 
1602
                    // sect 5.4 mime:multipartRelated
 
1603
                case 'part':
 
1604
                case 'mimeXml':
 
1605
                    // sect 5.6 mime:mimeXml
 
1606
                    // <mime:mimeXml part="nmtoken"?/>
 
1607
                    //
 
1608
                case 'documentation':
 
1609
                    break;
 
1610
 
 
1611
                default:
 
1612
                    // error
 
1613
                    break;
 
1614
                }
 
1615
 
 
1616
            case SCHEMA_DIME:
 
1617
                // DIME is defined in:
 
1618
                // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
 
1619
                // all DIME parts are children of wsdl:input, wsdl:output, etc.
 
1620
                // unsuported as of yet
 
1621
                switch ($qname->name) {
 
1622
                case 'message':
 
1623
                    // sect 4.1 dime:message
 
1624
                    // appears in binding section
 
1625
                    $this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs;
 
1626
                    break;
 
1627
 
 
1628
                default:
 
1629
                    break;
 
1630
                }
 
1631
 
 
1632
            default:
 
1633
                break;
 
1634
            }
 
1635
            break;
 
1636
 
 
1637
        case 'service':
 
1638
            $ns = $qname->prefix ? $this->wsdl->namespaces[$qname->prefix] : SCHEMA_WSDL;
 
1639
 
 
1640
            switch ($qname->name) {
 
1641
            case 'port':
 
1642
                // sect 2.6 wsdl:port attributes: name binding
 
1643
                $this->currentPort = $attrs['name'];
 
1644
                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs;
 
1645
                // XXX hack to deal with binding namespaces
 
1646
                $qn = new QName($attrs['binding']);
 
1647
                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name;
 
1648
                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->prefix;
 
1649
                break;
 
1650
 
 
1651
            case 'address':
 
1652
                $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs;
 
1653
                // what TYPE of port is it?  SOAP or HTTP?
 
1654
                $ns = $qname->prefix ? $this->wsdl->namespaces[$qname->prefix] : SCHEMA_WSDL;
 
1655
                switch ($ns) {
 
1656
                case SCHEMA_WSDL_HTTP:
 
1657
                    $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http';
 
1658
                    break;
 
1659
 
 
1660
                case SCHEMA_SOAP:
 
1661
                    $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
 
1662
                    break;
 
1663
 
 
1664
                default:
 
1665
                    // Shouldn't happen, we'll assume SOAP.
 
1666
                    $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
 
1667
                }
 
1668
 
 
1669
                break;
 
1670
 
 
1671
            case 'documentation':
 
1672
                break;
 
1673
 
 
1674
            default:
 
1675
                break;
 
1676
            }
 
1677
        }
 
1678
 
 
1679
        // Top level elements found under wsdl:definitions.
 
1680
        switch ($qname->name) {
 
1681
        case 'import':
 
1682
        case 'include':
 
1683
            // WSDL 2.1.1 wsdl:import, XML Schema 4.2.3 xsd:import, XML Schema
 
1684
            // 4.2.1 xsd:include attributes
 
1685
            $this->status = 'types';
 
1686
            if (isset($attrs['location']) || isset($attrs['schemaLocation'])) {
 
1687
                $uri = isset($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation'];
 
1688
                $location = @parse_url($uri);
 
1689
                if (!isset($location['scheme'])) {
 
1690
                    $base = @parse_url($this->uri);
 
1691
                    $uri = $this->mergeUrl($base, $uri);
 
1692
                }
 
1693
                if (isset($this->wsdl->imports[$uri])) {
 
1694
                    break;
 
1695
                }
 
1696
                $this->wsdl->imports[$uri] = $attrs;
 
1697
 
 
1698
                $import_parser_class = get_class($this);
 
1699
                $import_parser = new $import_parser_class($uri, $this->wsdl, $this->docs);
 
1700
                if ($import_parser->fault) {
 
1701
                    unset($this->wsdl->imports[$uri]);
 
1702
                    return false;
 
1703
                }
 
1704
            }
 
1705
            $this->status = 'types';
 
1706
            break;
 
1707
 
 
1708
        case 'types':
 
1709
            // sect 2.2 wsdl:types
 
1710
            $this->status = 'types';
 
1711
            break;
 
1712
 
 
1713
        case 'schema':
 
1714
            // We can hit this at the top level if we've been asked to
 
1715
            // import an XSD file.
 
1716
            if (!empty($attrs['targetNamespace'])) {
 
1717
                $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
 
1718
            } else {
 
1719
                $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
 
1720
            }
 
1721
            $this->wsdl->complexTypes[$this->schema] = array();
 
1722
            $this->wsdl->elements[$this->schema] = array();
 
1723
            $this->schema_stack[] = $qname->name;
 
1724
            $this->status = 'types';
 
1725
            break;
 
1726
 
 
1727
        case 'message':
 
1728
            // sect 2.3 wsdl:message attributes: name children:wsdl:part
 
1729
            $this->status = 'message';
 
1730
            if (isset($attrs['name'])) {
 
1731
                $this->currentMessage = $attrs['name'];
 
1732
                $this->wsdl->messages[$this->currentMessage] = array();
 
1733
            }
 
1734
            break;
 
1735
 
 
1736
        case 'portType':
 
1737
            // sect 2.4 wsdl:portType
 
1738
            // attributes: name
 
1739
            // children: wsdl:operation
 
1740
            $this->status = 'portType';
 
1741
            $this->currentPortType = $attrs['name'];
 
1742
            $this->wsdl->portTypes[$this->currentPortType] = array();
 
1743
            break;
 
1744
 
 
1745
        case 'binding':
 
1746
            // sect 2.5 wsdl:binding attributes: name type
 
1747
            // children: wsdl:operation soap:binding http:binding
 
1748
            if ($qname->prefix && $qname->prefix != $this->tns) {
 
1749
                break;
 
1750
            }
 
1751
            $this->status = 'binding';
 
1752
            $this->currentBinding = $attrs['name'];
 
1753
            $qn = new QName($attrs['type']);
 
1754
            $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name;
 
1755
            $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->prefix;
 
1756
            break;
 
1757
 
 
1758
        case 'service':
 
1759
            // sect 2.7 wsdl:service attributes: name children: ports
 
1760
            $this->currentService = $attrs['name'];
 
1761
            $this->wsdl->services[$this->currentService]['ports'] = array();
 
1762
            $this->status = 'service';
 
1763
            break;
 
1764
 
 
1765
        case 'definitions':
 
1766
            // sec 2.1 wsdl:definitions
 
1767
            // attributes: name targetNamespace xmlns:*
 
1768
            // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
 
1769
            $this->wsdl->definition = $attrs;
 
1770
            foreach ($attrs as $key => $value) {
 
1771
                if (strstr($key, 'xmlns:') !== false) {
 
1772
                    $qn = new QName($key);
 
1773
                    // XXX need to refactor ns handling.
 
1774
                    $this->wsdl->namespaces[$qn->name] = $value;
 
1775
                    $this->wsdl->ns[$value] = $qn->name;
 
1776
                    if ($key == 'targetNamespace' ||
 
1777
                        strcasecmp($value,SOAP_SCHEMA) == 0) {
 
1778
                        $this->soapns[] = $qn->name;
 
1779
                    } else {
 
1780
                        if (in_array($value, $this->_XMLSchema)) {
 
1781
                            $this->wsdl->xsd = $value;
 
1782
                        }
 
1783
                    }
 
1784
                }
 
1785
            }
 
1786
            if (isset($ns) && $ns) {
 
1787
                $namespace = 'xmlns:' . $ns;
 
1788
                if (!$this->wsdl->definition[$namespace]) {
 
1789
                    return $this->_raiseSoapFault("parse error, no namespace for $namespace", $this->uri);
 
1790
                }
 
1791
                $this->tns = $ns;
 
1792
            }
 
1793
            break;
 
1794
        }
 
1795
    }
 
1796
 
 
1797
    /**
 
1798
     * end-element handler.
 
1799
     */
 
1800
    function endElement($parser, $name)
 
1801
    {
 
1802
        $stacksize = count($this->element_stack);
 
1803
        if ($stacksize) {
 
1804
            if ($this->element_stack[$stacksize - 1] == 'definitions') {
 
1805
                $this->status = '';
 
1806
            }
 
1807
            array_pop($this->element_stack);
 
1808
        }
 
1809
 
 
1810
        if (stristr($name, 'schema')) {
 
1811
            array_pop($this->schema_stack);
 
1812
            $this->schema = '';
 
1813
        }
 
1814
 
 
1815
        if ($this->schema) {
 
1816
            array_pop($this->schema_stack);
 
1817
            if (count($this->schema_stack) <= 1) {
 
1818
                /* Correct the type for sequences with multiple
 
1819
                 * elements. */
 
1820
                if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])
 
1821
                    && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array'
 
1822
                    && array_key_exists('elements', $this->wsdl->complexTypes[$this->schema][$this->currentComplexType])
 
1823
                    && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) {
 
1824
                        $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
 
1825
                }
 
1826
            }
 
1827
            if (stristr($name, 'complexType')) {
 
1828
                $this->currentComplexType = '';
 
1829
                if (count($this->schema_element_stack)) {
 
1830
                    $this->currentElement = array_pop($this->schema_element_stack);
 
1831
                } else {
 
1832
                    $this->currentElement = '';
 
1833
                }
 
1834
            } elseif (stristr($name, 'element')) {
 
1835
                if (count($this->schema_element_stack)) {
 
1836
                    $this->currentElement = array_pop($this->schema_element_stack);
 
1837
                } else {
 
1838
                    $this->currentElement = '';
 
1839
                }
 
1840
            }
 
1841
        }
 
1842
    }
 
1843
 
 
1844
    /**
 
1845
     * Element content handler.
 
1846
     */
 
1847
    function characterData($parser, $data)
 
1848
    {
 
1849
        // Store the documentation in the WSDL file.
 
1850
        if ($this->currentTag == 'documentation') {
 
1851
            $data = trim(preg_replace('/\s+/', ' ', $data));
 
1852
            if (!strlen($data)) {
 
1853
                return;
 
1854
            }
 
1855
 
 
1856
            switch ($this->status) {
 
1857
            case 'service':
 
1858
                $ptr =& $this->wsdl->services[$this->currentService];
 
1859
                break;
 
1860
 
 
1861
            case 'portType':
 
1862
                $ptr =& $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation];
 
1863
                break;
 
1864
 
 
1865
            case 'binding':
 
1866
                $ptr =& $this->wsdl->bindings[$this->currentBinding];
 
1867
                break;
 
1868
 
 
1869
            case 'message':
 
1870
                $ptr =& $this->wsdl->messages[$this->currentMessage];
 
1871
                break;
 
1872
 
 
1873
            case 'operation':
 
1874
                break;
 
1875
 
 
1876
            case 'types':
 
1877
                if (isset($this->currentComplexType) &&
 
1878
                    isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
 
1879
                    if ($this->currentElement) {
 
1880
                        $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement];
 
1881
                    } else {
 
1882
                        $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType];
 
1883
                    }
 
1884
                }
 
1885
                break;
 
1886
            }
 
1887
 
 
1888
            if (isset($ptr)) {
 
1889
                if (!isset($ptr['documentation'])) {
 
1890
                    $ptr['documentation'] = '';
 
1891
                } else {
 
1892
                    $ptr['documentation'] .= ' ';
 
1893
                }
 
1894
                $ptr['documentation'] .= $data;
 
1895
            }
 
1896
        }
 
1897
    }
 
1898
 
 
1899
    /**
 
1900
     * $parsed is an array returned by parse_url().
 
1901
     *
 
1902
     * @access private
 
1903
     */
 
1904
    function mergeUrl($parsed, $path)
 
1905
    {
 
1906
        if (!is_array($parsed)) {
 
1907
            return false;
 
1908
        }
 
1909
 
 
1910
        $uri = '';
 
1911
        if (!empty($parsed['scheme'])) {
 
1912
            $sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
 
1913
            $uri = $parsed['scheme'] . $sep;
 
1914
        }
 
1915
 
 
1916
        if (isset($parsed['pass'])) {
 
1917
            $uri .= "$parsed[user]:$parsed[pass]@";
 
1918
        } elseif (isset($parsed['user'])) {
 
1919
            $uri .= "$parsed[user]@";
 
1920
        }
 
1921
 
 
1922
        if (isset($parsed['host'])) {
 
1923
            $uri .= $parsed['host'];
 
1924
        }
 
1925
        if (isset($parsed['port'])) {
 
1926
            $uri .= ":$parsed[port]";
 
1927
        }
 
1928
        if ($path[0] != '/' && isset($parsed['path'])) {
 
1929
            if ($parsed['path'][strlen($parsed['path']) - 1] != '/') {
 
1930
                $path = dirname($parsed['path']) . '/' . $path;
 
1931
            } else {
 
1932
                $path = $parsed['path'] . $path;
 
1933
            }
 
1934
            $path = $this->_normalize($path);
 
1935
        }
 
1936
        $sep = $path[0] == '/' ? '' : '/';
 
1937
        $uri .= $sep . $path;
 
1938
 
 
1939
        return $uri;
 
1940
    }
 
1941
 
 
1942
    function _normalize($path_str)
 
1943
    {
 
1944
        $pwd = '';
 
1945
        $strArr = preg_split('/(\/)/', $path_str, -1, PREG_SPLIT_NO_EMPTY);
 
1946
        $pwdArr = '';
 
1947
        $j = 0;
 
1948
        for ($i = 0; $i < count($strArr); $i++) {
 
1949
            if ($strArr[$i] != ' ..') {
 
1950
                if ($strArr[$i] != ' .') {
 
1951
                    $pwdArr[$j] = $strArr[$i];
 
1952
                    $j++;
 
1953
                }
 
1954
            } else {
 
1955
                array_pop($pwdArr);
 
1956
                $j--;
 
1957
            }
 
1958
        }
 
1959
        $pStr = implode('/', $pwdArr);
 
1960
        $pwd = (strlen($pStr) > 0) ? ('/' . $pStr) : '/';
 
1961
        return $pwd;
 
1962
    }
 
1963
 
 
1964
}
 
1965
 
 
1966
/**
 
1967
 * Parses the types and methods used in web service objects into the internal
 
1968
 * data structures used by SOAP_WSDL.
 
1969
 *
 
1970
 * Assumes the SOAP_WSDL class is unpopulated to start with.
 
1971
 *
 
1972
 * @author Chris Coe <info@intelligentstreaming.com>
 
1973
 */
 
1974
class SOAP_WSDL_ObjectParser extends SOAP_Base
 
1975
{
 
1976
    /**
 
1977
     * Target namespace for the WSDL document will have the following
 
1978
     * prefix.
 
1979
     */
 
1980
    var $tnsPrefix = 'tns';
 
1981
 
 
1982
    /**
 
1983
     * Reference to the SOAP_WSDL object to populate.
 
1984
     */
 
1985
    var $wsdl = null;
 
1986
 
 
1987
    /**
 
1988
     * Constructor.
 
1989
     *
 
1990
     * @param object|array $objects    Reference to the object or array of
 
1991
     *                                 objects to parse.
 
1992
     * @param SOAP_WSDL $wsdl          Reference to the SOAP_WSDL object to
 
1993
     *                                 populate.
 
1994
     * @param string $targetNamespace  The target namespace of schema types
 
1995
     *                                 etc.
 
1996
     * @param string $service_name     Name of the WSDL <service>.
 
1997
     * @param string $service_desc     Optional description of the WSDL
 
1998
     *                                 <service>.
 
1999
     */
 
2000
    function SOAP_WSDL_ObjectParser($objects, &$wsdl, $targetNamespace,
 
2001
                                    $service_name, $service_desc = '')
 
2002
    {
 
2003
        parent::SOAP_Base('WSDLOBJECTPARSER');
 
2004
 
 
2005
        $this->wsdl = &$wsdl;
 
2006
 
 
2007
        // Set up the SOAP_WSDL object
 
2008
        $this->_initialise($service_name);
 
2009
 
 
2010
        // Parse each web service object
 
2011
        $wsdl_ref = is_array($objects) ? $objects : array($objects);
 
2012
 
 
2013
        foreach ($wsdl_ref as $ref_item) {
 
2014
            if (!is_object($ref_item)) {
 
2015
                $this->_raiseSoapFault('Invalid web service object passed to object parser');
 
2016
                continue;
 
2017
            }
 
2018
 
 
2019
            if (!$this->_parse($ref_item, $targetNamespace, $service_name)) {
 
2020
                break;
 
2021
            }
 
2022
        }
 
2023
 
 
2024
        // Build bindings from abstract data.
 
2025
        if ($this->fault == null) {
 
2026
            $this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc);
 
2027
        }
 
2028
    }
 
2029
 
 
2030
    /**
 
2031
     * Initialise the SOAP_WSDL tree (destructive).
 
2032
     *
 
2033
     * If the object has already been initialised, the only effect
 
2034
     * will be to change the tns namespace to the new service name.
 
2035
     *
 
2036
     * @param  $service_name Name of the WSDL <service>
 
2037
     * @access private
 
2038
     */
 
2039
    function _initialise($service_name)
 
2040
    {
 
2041
        // Set up the basic namespaces that all WSDL definitions use.
 
2042
        $this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL;                                      // WSDL language
 
2043
        $this->wsdl->namespaces['soap'] = SCHEMA_SOAP;                                      // WSDL SOAP bindings
 
2044
        $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name;                 // Target namespace
 
2045
        $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces);           // XML Schema
 
2046
        $this->wsdl->namespaces[SOAP_BASE::SOAPENCPrefix()] = array_search(SOAP_BASE::SOAPENCPrefix(), $this->_namespaces); // SOAP types
 
2047
 
 
2048
        // XXX Refactor $namespace/$ns for Shane :-)
 
2049
        unset($this->wsdl->ns['urn:' . $service_name]);
 
2050
        $this->wsdl->ns += array_flip($this->wsdl->namespaces);
 
2051
 
 
2052
        // Imports are not implemented in WSDL generation from classes.
 
2053
        // *** <wsdl:import> ***
 
2054
    }
 
2055
 
 
2056
    /**
 
2057
     * Parser - takes a single object to add to tree (non-destructive).
 
2058
     *
 
2059
     * @access private
 
2060
     *
 
2061
     * @param object $object           Reference to the object to parse.
 
2062
     * @param string $schemaNamespace
 
2063
     * @param string $service_name     Name of the WSDL <service>.
 
2064
     */
 
2065
    function _parse($object, $schemaNamespace, $service_name)
 
2066
    {
 
2067
        // Create namespace prefix for the schema
 
2068
        list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}');
 
2069
 
 
2070
        // Parse all the types defined by the object in whatever
 
2071
        // schema language we are using (currently __typedef arrays)
 
2072
        // *** <wsdl:types> ***
 
2073
        foreach ($object->__typedef as $typeName => $typeValue) {
 
2074
            // Get/create namespace definition
 
2075
            list($nsPrefix, $typeName) = $this->_getTypeNs($typeName);
 
2076
 
 
2077
            // Create type definition
 
2078
            $this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName);
 
2079
            $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName];
 
2080
 
 
2081
            // According to Dmitri's documentation, __typedef comes in two
 
2082
            // flavors:
 
2083
            // Array = array(array("item" => "value"))
 
2084
            // Struct = array("item1" => "value1", "item2" => "value2", ...)
 
2085
            if (is_array($typeValue)) {
 
2086
                if (is_array(current($typeValue)) && count($typeValue) == 1
 
2087
                    && count(current($typeValue)) == 1) {
 
2088
                    // It's an array
 
2089
                    $thisType['type'] = 'Array';
 
2090
                    $nsType = current(current($typeValue));
 
2091
                    list($nsPrefix, $typeName) = $this->_getTypeNs($nsType);
 
2092
                    $thisType['namespace'] = $nsPrefix;
 
2093
                    $thisType['arrayType'] = $typeName . '[]';
 
2094
                } elseif (!is_array(current($typeValue))) {
 
2095
                    // It's a struct
 
2096
                    $thisType['type'] = 'Struct';
 
2097
                    $thisType['order'] = 'all';
 
2098
                    $thisType['namespace'] = $nsPrefix;
 
2099
                    $thisType['elements'] = array();
 
2100
 
 
2101
                    foreach ($typeValue as $elementName => $elementType) {
 
2102
                        list($nsPrefix, $typeName) = $this->_getTypeNs($elementType);
 
2103
                        $thisType['elements'][$elementName]['name'] = $elementName;
 
2104
                        $thisType['elements'][$elementName]['type'] = $typeName;
 
2105
                        $thisType['elements'][$elementName]['namespace'] = $nsPrefix;
 
2106
                    }
 
2107
                } else {
 
2108
                    // It's erroneous
 
2109
                    return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
 
2110
                }
 
2111
            } else {
 
2112
                // It's erroneous
 
2113
                return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
 
2114
            }
 
2115
        }
 
2116
 
 
2117
        // Create an empty element array with the target namespace
 
2118
        // prefix, to match the results of WSDL parsing.
 
2119
        $this->wsdl->elements[$schPrefix] = array();
 
2120
 
 
2121
        // Populate tree with message information
 
2122
        // *** <wsdl:message> ***
 
2123
        foreach ($object->__dispatch_map as $operationName => $messages) {
 
2124
            // We need at least 'in' and 'out' parameters.
 
2125
            if (!isset($messages['in']) || !isset($messages['out'])) {
 
2126
                return $this->_raiseSoapFault('The dispatch map for the method "' . $operationName . '" is missing an "in" or "out" definition.', 'urn:' . get_class($object));
 
2127
            }
 
2128
            foreach ($messages as $messageType => $messageParts) {
 
2129
                unset($thisMessage);
 
2130
 
 
2131
                switch ($messageType) {
 
2132
                case 'in':
 
2133
                    $this->wsdl->messages[$operationName . 'Request'] = array();
 
2134
                    $thisMessage =& $this->wsdl->messages[$operationName . 'Request'];
 
2135
                    break;
 
2136
 
 
2137
                case 'out':
 
2138
                    $this->wsdl->messages[$operationName . 'Response'] = array();
 
2139
                    $thisMessage =& $this->wsdl->messages[$operationName . 'Response'];
 
2140
                    break;
 
2141
 
 
2142
                case 'alias':
 
2143
                    // Do nothing
 
2144
                    break;
 
2145
 
 
2146
                default:
 
2147
                    // Error condition
 
2148
                    break;
 
2149
                }
 
2150
 
 
2151
                if (isset($thisMessage)) {
 
2152
                    foreach ($messageParts as $partName => $partType) {
 
2153
                        list ($nsPrefix, $typeName) = $this->_getTypeNs($partType);
 
2154
 
 
2155
                        $thisMessage[$partName] = array(
 
2156
                            'name' => $partName,
 
2157
                            'type' => $typeName,
 
2158
                            'namespace' => $nsPrefix
 
2159
                            );
 
2160
                    }
 
2161
                }
 
2162
            }
 
2163
        }
 
2164
 
 
2165
        // Populate tree with portType information
 
2166
        // XXX Current implementation only supports one portType that
 
2167
        // encompasses all of the operations available.
 
2168
        // *** <wsdl:portType> ***
 
2169
        if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) {
 
2170
            $this->wsdl->portTypes[$service_name . 'Port'] = array();
 
2171
        }
 
2172
        $thisPortType =& $this->wsdl->portTypes[$service_name . 'Port'];
 
2173
 
 
2174
        foreach ($object->__dispatch_map as $operationName => $messages) {
 
2175
            $thisPortType[$operationName] = array('name' => $operationName);
 
2176
 
 
2177
            foreach ($messages as $messageType => $messageParts) {
 
2178
                switch ($messageType) {
 
2179
                case 'in':
 
2180
                    $thisPortType[$operationName]['input'] = array(
 
2181
                        'message' => $operationName . 'Request',
 
2182
                        'namespace' => $this->tnsPrefix);
 
2183
                    break;
 
2184
 
 
2185
                case 'out':
 
2186
                    $thisPortType[$operationName]['output'] = array(
 
2187
                        'message' => $operationName . 'Response',
 
2188
                        'namespace' => $this->tnsPrefix);
 
2189
                    break;
 
2190
                }
 
2191
            }
 
2192
        }
 
2193
 
 
2194
        return true;
 
2195
    }
 
2196
 
 
2197
    /**
 
2198
     * Takes all the abstract WSDL data and builds concrete bindings and
 
2199
     * services (destructive).
 
2200
     *
 
2201
     * @access private
 
2202
     * @todo Current implementation discards $service_desc.
 
2203
     *
 
2204
     * @param string $schemaNamespace  Namespace for types etc.
 
2205
     * @param string $service_name     Name of the WSDL <service>.
 
2206
     * @param string $service_desc     Optional description of the WSDL
 
2207
     *                                 <service>.
 
2208
     */
 
2209
    function _generateBindingsAndServices($schemaNamespace, $service_name,
 
2210
                                          $service_desc = '')
 
2211
    {
 
2212
        // Populate tree with bindings information
 
2213
        // XXX Current implementation only supports one binding that
 
2214
        // matches the single portType and all of its operations.
 
2215
        // XXX Is this the correct use of $schemaNamespace here?
 
2216
        // *** <wsdl:binding> ***
 
2217
        $this->wsdl->bindings[$service_name . 'Binding'] = array(
 
2218
                'type' => $service_name . 'Port',
 
2219
                'namespace' => $this->tnsPrefix,
 
2220
                'style' => 'rpc',
 
2221
                'transport' => SCHEMA_SOAP_HTTP,
 
2222
                'operations' => array());
 
2223
        $thisBinding =& $this->wsdl->bindings[$service_name . 'Binding'];
 
2224
 
 
2225
        foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData) {
 
2226
            $thisBinding['operations'][$operationName] = array(
 
2227
                'soapAction' => $schemaNamespace . '#' . $operationName,
 
2228
                'style' => $thisBinding['style']);
 
2229
 
 
2230
            foreach (array('input', 'output') as $messageType)
 
2231
                if (isset($operationData[$messageType])) {
 
2232
                    $thisBinding['operations'][$operationName][$messageType] = array(
 
2233
                            'use' => 'encoded',
 
2234
                            'namespace' => $schemaNamespace,
 
2235
                            'encodingStyle' => SOAP_SCHEMA_ENCODING);
 
2236
                }
 
2237
        }
 
2238
 
 
2239
        // Populate tree with service information
 
2240
        // XXX Current implementation supports one service which groups
 
2241
        // all of the ports together, one port per binding
 
2242
        // *** <wsdl:service> ***
 
2243
 
 
2244
        $this->wsdl->services[$service_name . 'Service'] = array('ports' => array());
 
2245
        $thisService =& $this->wsdl->services[$service_name . 'Service']['ports'];
 
2246
        $https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) ||
 
2247
            getenv('SSL_PROTOCOL_VERSION');
 
2248
 
 
2249
        foreach ($this->wsdl->bindings as $bindingName => $bindingData) {
 
2250
            $thisService[$bindingData['type']] = array(
 
2251
                    'name' => $bindingData['type'],
 
2252
                    'binding' => $bindingName,
 
2253
                    'namespace' => $this->tnsPrefix,
 
2254
                    'address' => array('location' =>
 
2255
                        ($https ? 'https://' : 'http://') .
 
2256
                        $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] .
 
2257
                        (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')),
 
2258
                    'type' => 'soap');
 
2259
        }
 
2260
 
 
2261
        // Set service
 
2262
        $this->wsdl->set_service($service_name . 'Service');
 
2263
        $this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix];
 
2264
 
 
2265
        // Create WSDL definition
 
2266
        // *** <wsdl:definitions> ***
 
2267
 
 
2268
        $this->wsdl->definition = array(
 
2269
                'name' => $service_name,
 
2270
                'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix],
 
2271
                'xmlns' => SCHEMA_WSDL);
 
2272
 
 
2273
        foreach ($this->wsdl->namespaces as $nsPrefix => $namespace) {
 
2274
            $this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace;
 
2275
        }
 
2276
    }
 
2277
 
 
2278
    /**
 
2279
     * This function is adapted from Dmitri V's implementation of
 
2280
     * DISCO/WSDL generation. It separates namespace from type name in
 
2281
     * a __typedef key and creates a new namespace entry in the WSDL
 
2282
     * structure if the namespace has not been used before. The
 
2283
     * namespace prefix and type name are returned. If no namespace is
 
2284
     * specified, xsd is assumed.
 
2285
     *
 
2286
     * We will not need this function anymore once __typedef is
 
2287
     * eliminated.
 
2288
     */
 
2289
    function _getTypeNs($type)
 
2290
    {
 
2291
        preg_match_all('/\{(.*)\}/sm', $type, $m);
 
2292
        if (!empty($m[1][0])) {
 
2293
            if (!isset($this->wsdl->ns[$m[1][0]])) {
 
2294
                $ns_pref = 'ns' . count($this->wsdl->namespaces);
 
2295
                $this->wsdl->ns[$m[1][0]] = $ns_pref;
 
2296
                $this->wsdl->namespaces[$ns_pref] = $m[1][0];
 
2297
            }
 
2298
            $typens = $this->wsdl->ns[$m[1][0]];
 
2299
            $type = str_replace($m[0][0], '', $type);
 
2300
        } else {
 
2301
            $typens = 'xsd';
 
2302
        }
 
2303
 
 
2304
        return array($typens, $type);
 
2305
    }
 
2306
 
 
2307
}