Test Failed
Push — main ( c8394f...8477f1 )
by Rafael
66:21
created

soap_transport_http::getResponse()   F

Complexity

Conditions 84
Paths 8892

Size

Total Lines 371
Code Lines 251

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 84
eloc 251
nc 8892
nop 0
dl 0
loc 371
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
$Id: nusoap.php,v 1.124 2010/04/26 20:15:08 snichol Exp $
5
6
NuSOAP - Web Services Toolkit for PHP
7
8
Copyright (c) 2002 NuSphere Corporation
9
10
This library is free software; you can redistribute it and/or
11
modify it under the terms of the GNU Lesser General Public
12
License as published by the Free Software Foundation; either
13
version 2.1 of the License, or (at your option) any later version.
14
15
This library is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
Lesser General Public License for more details.
19
20
You should have received a copy of the GNU Lesser General Public
21
License along with this library; if not, write to the Free Software
22
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24
The NuSOAP project home is:
25
http://sourceforge.net/projects/nusoap/
26
27
The primary support for NuSOAP is the Help forum on the project home page.
28
29
If you have any questions or comments, please email:
30
31
Dietrich Ayala
32
[email protected]
33
http://dietrich.ganx4.com/nusoap
34
35
NuSphere Corporation
36
http://www.nusphere.com
37
38
*/
39
40
/*
41
 *	Some of the standards implmented in whole or part by NuSOAP:
42
 *
43
 *	SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
44
 *	WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
45
 *	SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
46
 *	XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
47
 *	Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
48
 *	XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
49
 *	RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
50
 *	RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
51
 *	RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
52
 */
53
54
/* load classes
55
56
// necessary classes
57
require_once('class.soapclient.php');
58
require_once('class.soap_val.php');
59
require_once('class.soap_parser.php');
60
require_once('class.soap_fault.php');
61
62
// transport classes
63
require_once('class.soap_transport_http.php');
64
65
// optional add-on classes
66
require_once('class.xmlschema.php');
67
require_once('class.wsdl.php');
68
69
// server class
70
require_once('class.soap_server.php');*/
71
72
// class variable emulation
73
// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
74
$GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
75
76
77
/**
78
 *
79
 * nusoap_base
80
 *
81
 * @author   Dietrich Ayala <[email protected]>
82
 * @author   Scott Nichol <[email protected]>
83
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
84
 * @access   public
85
 */
86
class nusoap_base
87
{
88
    /**
89
     * Identification for HTTP headers.
90
     *
91
     * @var string
92
     * @access private
93
     */
94
    var $title = 'NuSOAP';
95
    /**
96
     * Version for HTTP headers.
97
     *
98
     * @var string
99
     * @access private
100
     */
101
    var $version = '0.9.11';
102
    /**
103
     * CVS revision for HTTP headers.
104
     *
105
     * @var string
106
     * @access private
107
     */
108
    var $revision = '$Revision: 1.123 $';
109
    /**
110
     * Current error string (manipulated by getError/setError)
111
     *
112
     * @var string
113
     * @access private
114
     */
115
    var $error_str = '';
116
    /**
117
     * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
118
     *
119
     * @var string
120
     * @access private
121
     */
122
    var $debug_str = '';
123
    /**
124
     * toggles automatic encoding of special characters as entities
125
     * (should always be true, I think)
126
     *
127
     * @var boolean
128
     * @access private
129
     */
130
    var $charencoding = true;
131
    /**
132
     * the debug level for this instance
133
     *
134
     * @var    integer
135
     * @access private
136
     */
137
    var $debugLevel;
138
139
    /**
140
     * set schema version
141
     *
142
     * @var      string
143
     * @access   public
144
     */
145
    var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
146
147
    /**
148
     * charset encoding for outgoing messages
149
     *
150
     * @var      string
151
     * @access   public
152
     */
153
    var $soap_defencoding = 'ISO-8859-1';
154
    //var $soap_defencoding = 'UTF-8';
155
156
    /**
157
     * namespaces in an array of prefix => uri
158
     *
159
     * this is "seeded" by a set of constants, but it may be altered by code
160
     *
161
     * @var      array
162
     * @access   public
163
     */
164
    var $namespaces = array(
165
        'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
166
        'xsd' => 'http://www.w3.org/2001/XMLSchema',
167
        'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
168
        'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
169
    );
170
171
    /**
172
     * namespaces used in the current context, e.g. during serialization
173
     *
174
     * @var      array
175
     * @access   private
176
     */
177
    var $usedNamespaces = array();
178
179
    /**
180
     * XML Schema types in an array of uri => (array of xml type => php type)
181
     * is this legacy yet?
182
     * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
183
     *
184
     * @var      array
185
     * @access   public
186
     */
187
    var $typemap = array(
188
        'http://www.w3.org/2001/XMLSchema' => array(
189
            'string' => 'string', 'boolean' => 'boolean', 'float' => 'double', 'double' => 'double', 'decimal' => 'double',
190
            'duration' => '', 'dateTime' => 'string', 'time' => 'string', 'date' => 'string', 'gYearMonth' => '',
191
            'gYear' => '', 'gMonthDay' => '', 'gDay' => '', 'gMonth' => '', 'hexBinary' => 'string', 'base64Binary' => 'string',
192
            // abstract "any" types
193
            'anyType' => 'string', 'anySimpleType' => 'string',
194
            // derived datatypes
195
            'normalizedString' => 'string', 'token' => 'string', 'language' => '', 'NMTOKEN' => '', 'NMTOKENS' => '', 'Name' => '', 'NCName' => '', 'ID' => '',
196
            'IDREF' => '', 'IDREFS' => '', 'ENTITY' => '', 'ENTITIES' => '', 'integer' => 'integer', 'nonPositiveInteger' => 'integer',
197
            'negativeInteger' => 'integer', 'long' => 'integer', 'int' => 'integer', 'short' => 'integer', 'byte' => 'integer', 'nonNegativeInteger' => 'integer',
198
            'unsignedLong' => '', 'unsignedInt' => '', 'unsignedShort' => '', 'unsignedByte' => '', 'positiveInteger' => ''),
199
        'http://www.w3.org/2000/10/XMLSchema' => array(
200
            'i4' => '', 'int' => 'integer', 'boolean' => 'boolean', 'string' => 'string', 'double' => 'double',
201
            'float' => 'double', 'dateTime' => 'string',
202
            'timeInstant' => 'string', 'base64Binary' => 'string', 'base64' => 'string', 'ur-type' => 'array'),
203
        'http://www.w3.org/1999/XMLSchema' => array(
204
            'i4' => '', 'int' => 'integer', 'boolean' => 'boolean', 'string' => 'string', 'double' => 'double',
205
            'float' => 'double', 'dateTime' => 'string',
206
            'timeInstant' => 'string', 'base64Binary' => 'string', 'base64' => 'string', 'ur-type' => 'array'),
207
        'http://soapinterop.org/xsd' => array('SOAPStruct' => 'struct'),
208
        'http://schemas.xmlsoap.org/soap/encoding/' => array('base64' => 'string', 'array' => 'array', 'Array' => 'array'),
209
        'http://xml.apache.org/xml-soap' => array('Map')
210
    );
211
212
    /**
213
     * XML entities to convert
214
     *
215
     * @var      array
216
     * @access   public
217
     * @deprecated
218
     * @see    expandEntities
219
     */
220
    var $xmlEntities = array('quot' => '"', 'amp' => '&',
221
        'lt' => '<', 'gt' => '>', 'apos' => "'");
222
223
    /**
224
     * HTTP Content-type to be used for SOAP calls and responses
225
     *
226
     * @var string
227
     */
228
    var $contentType = "text/xml";
229
230
231
    /**
232
     * constructor
233
     *
234
     * @access    public
235
     */
236
    function __construct()
237
    {
238
        $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
239
    }
240
241
    /**
242
     * gets the global debug level, which applies to future instances
243
     *
244
     * @return    integer    Debug level 0-9, where 0 turns off
245
     * @access    public
246
     */
247
    function getGlobalDebugLevel()
248
    {
249
        return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
250
    }
251
252
    /**
253
     * sets the global debug level, which applies to future instances
254
     *
255
     * @param    int $level Debug level 0-9, where 0 turns off
256
     * @access    public
257
     */
258
    function setGlobalDebugLevel($level)
259
    {
260
        $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
261
    }
262
263
    /**
264
     * gets the debug level for this instance
265
     *
266
     * @return    int    Debug level 0-9, where 0 turns off
267
     * @access    public
268
     */
269
    function getDebugLevel()
270
    {
271
        return $this->debugLevel;
272
    }
273
274
    /**
275
     * sets the debug level for this instance
276
     *
277
     * @param    int $level Debug level 0-9, where 0 turns off
278
     * @access    public
279
     */
280
    function setDebugLevel($level)
281
    {
282
        $this->debugLevel = $level;
283
    }
284
285
    /**
286
     * adds debug data to the instance debug string with formatting
287
     *
288
     * @param    string $string debug data
289
     * @access   private
290
     */
291
    function debug($string)
292
    {
293
        if ($this->debugLevel > 0) {
294
            $this->appendDebug($this->getmicrotime() . ' ' . get_class($this) . ": $string\n");
295
        }
296
    }
297
298
    /**
299
     * adds debug data to the instance debug string without formatting
300
     *
301
     * @param    string $string debug data
302
     * @access   public
303
     */
304
    function appendDebug($string)
305
    {
306
        if ($this->debugLevel > 0) {
307
            // it would be nice to use a memory stream here to use
308
            // memory more efficiently
309
            $this->debug_str .= $string;
310
        }
311
    }
312
313
    /**
314
     * clears the current debug data for this instance
315
     *
316
     * @access   public
317
     */
318
    function clearDebug()
319
    {
320
        // it would be nice to use a memory stream here to use
321
        // memory more efficiently
322
        $this->debug_str = '';
323
    }
324
325
    /**
326
     * gets the current debug data for this instance
327
     *
328
     * @return   string data
329
     * @access   public
330
     */
331
    function &getDebug()
332
    {
333
        // it would be nice to use a memory stream here to use
334
        // memory more efficiently
335
        return $this->debug_str;
336
    }
337
338
    /**
339
     * gets the current debug data for this instance as an XML comment
340
     * this may change the contents of the debug data
341
     *
342
     * @return   string data as an XML comment
343
     * @access   public
344
     */
345
    function &getDebugAsXMLComment()
346
    {
347
        // it would be nice to use a memory stream here to use
348
        // memory more efficiently
349
        while (strpos($this->debug_str, '--')) {
350
            $this->debug_str = str_replace('--', '- -', $this->debug_str);
351
        }
352
        $ret = "<!--\n" . $this->debug_str . "\n-->";
353
        return $ret;
354
    }
355
356
    /**
357
     * expands entities, e.g. changes '<' to '&lt;'.
358
     *
359
     * @param    string $val The string in which to expand entities.
360
     * @access    private
361
     */
362
    function expandEntities($val)
363
    {
364
        if ($this->charencoding) {
365
            $val = str_replace('&', '&amp;', $val);
366
            $val = str_replace("'", '&apos;', $val);
367
            $val = str_replace('"', '&quot;', $val);
368
            $val = str_replace('<', '&lt;', $val);
369
            $val = str_replace('>', '&gt;', $val);
370
        }
371
        return $val;
372
    }
373
374
    /**
375
     * returns error string if present
376
     *
377
     * @return   false|string error string or false
378
     * @access   public
379
     */
380
    function getError()
381
    {
382
        if ($this->error_str != '') {
383
            return $this->error_str;
384
        }
385
        return false;
386
    }
387
388
    /**
389
     * sets error string
390
     *
391
     * @return   void
392
     * @access   private
393
     */
394
    function setError($str)
395
    {
396
        $this->error_str = $str;
397
    }
398
399
    /**
400
     * detect if array is a simple array or a struct (associative array)
401
     *
402
     * @param    mixed $val The PHP array
403
     * @return    string    (arraySimple|arrayStruct)
404
     * @access    private
405
     */
406
    function isArraySimpleOrStruct($val)
407
    {
408
        $keyList = array_keys($val);
409
        foreach ($keyList as $keyListValue) {
410
            if (!is_int($keyListValue)) {
411
                return 'arrayStruct';
412
            }
413
        }
414
        return 'arraySimple';
415
    }
416
417
    /**
418
     * serializes PHP values in accordance w/ section 5. Type information is
419
     * not serialized if $use == 'literal'.
420
     *
421
     * @param    mixed $val The value to serialize
422
     * @param    string $name The name (local part) of the XML element
423
     * @param    string $type The XML schema type (local part) for the element
424
     * @param    string $name_ns The namespace for the name of the XML element
425
     * @param    string $type_ns The namespace for the type of the element
426
     * @param    array $attributes The attributes to serialize as name=>value pairs
427
     * @param    string $use The WSDL "use" (encoded|literal)
428
     * @param    boolean $soapval Whether this is called from soapval.
429
     * @return    string    The serialized element, possibly with child elements
430
     * @access    public
431
     */
432
    function serialize_val($val, $name = false, $type = false, $name_ns = false, $type_ns = false, $attributes = false, $use = 'encoded', $soapval = false)
433
    {
434
        $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
435
        $this->appendDebug('value=' . $this->varDump($val));
436
        $this->appendDebug('attributes=' . $this->varDump($attributes));
437
438
        if (is_object($val) && get_class($val) == 'soapval' && (!$soapval)) {
439
            $this->debug("serialize_val: serialize soapval");
440
            $xml = $val->serialize($use);
441
            $this->appendDebug($val->getDebug());
442
            $val->clearDebug();
443
            $this->debug("serialize_val of soapval returning $xml");
444
            return $xml;
445
        }
446
        // force valid name if necessary
447
        if (is_numeric($name)) {
448
            $name = '__numeric_' . $name;
449
        } elseif (!$name) {
450
            $name = 'noname';
451
        }
452
        // if name has ns, add ns prefix to name
453
        $xmlns = '';
454
        if ($name_ns) {
455
            $prefix = 'nu' . rand(1000, 9999);
456
            $name = $prefix . ':' . $name;
457
            $xmlns .= " xmlns:$prefix=\"$name_ns\"";
458
        }
459
        // if type is prefixed, create type prefix
460
        if ($type_ns != '' && $type_ns == $this->namespaces['xsd']) {
461
            // need to fix this. shouldn't default to xsd if no ns specified
462
            // w/o checking against typemap
463
            $type_prefix = 'xsd';
464
        } elseif ($type_ns) {
465
            $type_prefix = 'ns' . rand(1000, 9999);
466
            $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
467
        }
468
        // serialize attributes if present
469
        $atts = '';
470
        if ($attributes) {
471
            foreach ($attributes as $k => $v) {
472
                $atts .= " $k=\"" . $this->expandEntities($v) . '"';
473
            }
474
        }
475
        // serialize null value
476
        if (is_null($val)) {
477
            $this->debug("serialize_val: serialize null");
478
            if ($use == 'literal') {
479
                // TODO: depends on minOccurs
480
                $xml = "<$name$xmlns$atts/>";
481
            } else {
482
                if (isset($type) && isset($type_prefix)) {
483
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
484
                } else {
485
                    $type_str = '';
486
                }
487
                $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
488
            }
489
            $this->debug("serialize_val returning $xml");
490
            return $xml;
491
        }
492
        // serialize if an xsd built-in primitive type
493
        if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])) {
494
            $this->debug("serialize_val: serialize xsd built-in primitive type");
495
            if (is_bool($val)) {
496
                if ($type == 'boolean') {
497
                    $val = $val ? 'true' : 'false';
498
                } elseif (!$val) {
499
                    $val = 0;
500
                }
501
            } elseif (is_string($val)) {
502
                $val = $this->expandEntities($val);
503
            }
504
            if ($use == 'literal') {
505
                $xml = "<$name$xmlns$atts>$val</$name>";
506
            } else {
507
                $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
508
            }
509
            $this->debug("serialize_val returning $xml");
510
            return $xml;
511
        }
512
        // detect type and serialize
513
        $xml = '';
514
        switch (true) {
515
            case (is_bool($val) || $type == 'boolean'):
516
                $this->debug("serialize_val: serialize boolean");
517
                if ($type == 'boolean') {
518
                    $val = $val ? 'true' : 'false';
519
                } elseif (!$val) {
520
                    $val = 0;
521
                }
522
                if ($use == 'literal') {
523
                    $xml .= "<$name$xmlns$atts>$val</$name>";
524
                } else {
525
                    $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
526
                }
527
                break;
528
            case (is_int($val) || is_long($val) || $type == 'int'):
529
                $this->debug("serialize_val: serialize int");
530
                if ($use == 'literal') {
531
                    $xml .= "<$name$xmlns$atts>$val</$name>";
532
                } else {
533
                    $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
534
                }
535
                break;
536
            case (is_float($val) || is_double($val) || $type == 'float'):
537
                $this->debug("serialize_val: serialize float");
538
                if ($use == 'literal') {
539
                    $xml .= "<$name$xmlns$atts>$val</$name>";
540
                } else {
541
                    $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
542
                }
543
                break;
544
            case (is_string($val) || $type == 'string'):
545
                $this->debug("serialize_val: serialize string");
546
                $val = $this->expandEntities($val);
547
                if ($use == 'literal') {
548
                    $xml .= "<$name$xmlns$atts>$val</$name>";
549
                } else {
550
                    $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
551
                }
552
                break;
553
            case is_object($val):
554
                $this->debug("serialize_val: serialize object");
555
                $pXml = "";
556
                if (get_class($val) == 'soapval') {
557
                    $this->debug("serialize_val: serialize soapval object");
558
                    $pXml = $val->serialize($use);
559
                    $this->appendDebug($val->getDebug());
560
                    $val->clearDebug();
561
                } else {
562
                    if (!$name) {
563
                        $name = get_class($val);
564
                        $this->debug("In serialize_val, used class name $name as element name");
565
                    } else {
566
                        $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
567
                    }
568
                    foreach (get_object_vars($val) as $k => $v) {
569
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
570
                    }
571
                }
572
                if (isset($type) && isset($type_prefix)) {
573
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
574
                } else {
575
                    $type_str = '';
576
                }
577
                if ($use == 'literal') {
578
                    $xml .= "<$name$xmlns$atts>$pXml</$name>";
579
                } else {
580
                    $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
581
                }
582
                break;
583
            case (is_array($val) || $type):
584
                // detect if struct or array
585
                $valueType = $this->isArraySimpleOrStruct($val);
586
                if ($valueType == 'arraySimple' || preg_match('/^ArrayOf/', $type)) {
587
                    $this->debug("serialize_val: serialize array");
588
                    $i = 0;
589
                    if (is_array($val) && count($val) > 0) {
590
                        $array_types = array ();
591
                        $tt_ns = "";
592
                        $tt = "";
593
                        foreach ($val as $v) {
594
                            if (is_object($v) && get_class($v) == 'soapval') {
595
                                $tt_ns = $v->type_ns;
596
                                $tt = $v->type;
597
                            } elseif (is_array($v)) {
598
                                $tt = $this->isArraySimpleOrStruct($v);
599
                            } else {
600
                                $tt = gettype($v);
601
                            }
602
                            $array_types[$tt] = 1;
603
                            // TODO: for literal, the name should be $name
604
                            $xml .= $this->serialize_val($v, 'item', false, false, false, false, $use);
605
                            ++$i;
606
                        }
607
                        if (count($array_types) > 1) {
608
                            $array_typename = 'xsd:anyType';
609
                        } elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
610
                            if ($tt == 'integer') {
611
                                $tt = 'int';
612
                            }
613
                            $array_typename = 'xsd:' . $tt;
614
                        } elseif (isset($tt) && $tt == 'arraySimple') {
615
                            $array_typename = 'SOAP-ENC:Array';
616
                        } elseif (isset($tt) && $tt == 'arrayStruct') {
617
                            $array_typename = 'unnamed_struct_use_soapval';
618
                        } else {
619
                            // if type is prefixed, create type prefix
620
                            if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']) {
621
                                $array_typename = 'xsd:' . $tt;
622
                            } elseif ($tt_ns) {
623
                                $tt_prefix = 'ns' . rand(1000, 9999);
624
                                $array_typename = "$tt_prefix:$tt";
625
                                $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
626
                            } else {
627
                                $array_typename = $tt;
628
                            }
629
                        }
630
                        $array_type = $i;
631
                        if ($use == 'literal') {
632
                            $type_str = '';
633
                        } elseif (isset($type) && isset($type_prefix)) {
634
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
635
                        } else {
636
                            $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"" . $array_typename . "[$array_type]\"";
637
                        }
638
                        // empty array
639
                    } else {
640
                        if ($use == 'literal') {
641
                            $type_str = '';
642
                        } elseif (isset($type) && isset($type_prefix)) {
643
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
644
                        } else {
645
                            $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
646
                        }
647
                    }
648
                    // TODO: for array in literal, there is no wrapper here
649
                    $xml = "<$name$xmlns$type_str$atts>" . $xml . "</$name>";
650
                } else {
651
                    // got a struct
652
                    $this->debug("serialize_val: serialize struct");
653
                    if (isset($type) && isset($type_prefix)) {
654
                        $type_str = " xsi:type=\"$type_prefix:$type\"";
655
                    } else {
656
                        $type_str = '';
657
                    }
658
                    if ($use == 'literal') {
659
                        $xml .= "<$name$xmlns$atts>";
660
                    } else {
661
                        $xml .= "<$name$xmlns$type_str$atts>";
662
                    }
663
                    foreach ($val as $k => $v) {
664
                        // Apache Map
665
                        if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
666
                            $xml .= '<item>';
667
                            $xml .= $this->serialize_val($k, 'key', false, false, false, false, $use);
668
                            $xml .= $this->serialize_val($v, 'value', false, false, false, false, $use);
669
                            $xml .= '</item>';
670
                        } else {
671
                            $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
672
                        }
673
                    }
674
                    $xml .= "</$name>";
675
                }
676
                break;
677
            default:
678
                $this->debug("serialize_val: serialize unknown");
679
                $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
680
                break;
681
        }
682
        $this->debug("serialize_val returning $xml");
683
        return $xml;
684
    }
685
686
    /**
687
     * serializes a message
688
     *
689
     * @param string $body the XML of the SOAP body
690
     * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
691
     * @param array $namespaces optional the namespaces used in generating the body and headers
692
     * @param string $style optional (rpc|document)
693
     * @param string $use optional (encoded|literal)
694
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
695
     * @return string the message
696
     * @access public
697
     */
698
    function serializeEnvelope($body, $headers = false, $namespaces = array(), $style = 'rpc', $use = 'encoded', $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/')
699
    {
700
        // TODO: add an option to automatically run utf8_encode on $body and $headers
701
        // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
702
        // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
703
704
        $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
705
        $this->debug("headers:");
706
        $this->appendDebug($this->varDump($headers));
707
        $this->debug("namespaces:");
708
        $this->appendDebug($this->varDump($namespaces));
709
710
        // serialize namespaces
711
        $ns_string = '';
712
        foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
713
            $ns_string .= " xmlns:$k=\"$v\"";
714
        }
715
        if ($encodingStyle) {
716
            $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
717
        }
718
719
        // serialize headers
720
        if ($headers) {
721
            if (is_array($headers)) {
722
                $xml = '';
723
                foreach ($headers as $k => $v) {
724
                    if (is_object($v) && get_class($v) == 'soapval') {
725
                        $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
726
                    } else {
727
                        $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
728
                    }
729
                }
730
                $headers = $xml;
731
                $this->debug("In serializeEnvelope, serialized array of headers to $headers");
732
            }
733
            $headers = "<SOAP-ENV:Header>" . $headers . "</SOAP-ENV:Header>";
734
        }
735
        // serialize envelope
736
        return
737
            '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . ">" .
738
            '<SOAP-ENV:Envelope' . $ns_string . ">" .
739
            $headers .
740
            "<SOAP-ENV:Body>" .
741
            $body .
742
            "</SOAP-ENV:Body>" .
743
            "</SOAP-ENV:Envelope>";
744
    }
745
746
    /**
747
     * formats a string to be inserted into an HTML stream
748
     *
749
     * @param string $str The string to format
750
     * @return string The formatted string
751
     * @access public
752
     * @deprecated
753
     */
754
    function formatDump($str)
755
    {
756
        $str = htmlspecialchars($str);
757
        return nl2br($str);
758
    }
759
760
    /**
761
     * contracts (changes namespace to prefix) a qualified name
762
     *
763
     * @param    string $qname qname
764
     * @return    string contracted qname
765
     * @access   private
766
     */
767
    function contractQname($qname)
768
    {
769
        // get element namespace
770
        //$this->xdebug("Contract $qname");
771
        if (strrpos($qname, ':')) {
772
            // get unqualified name
773
            $name = substr($qname, strrpos($qname, ':') + 1);
774
            // get ns
775
            $ns = substr($qname, 0, strrpos($qname, ':'));
776
            $p = $this->getPrefixFromNamespace($ns);
777
            if ($p) {
778
                return $p . ':' . $name;
779
            }
780
        }
781
        return $qname;
782
    }
783
784
    /**
785
     * expands (changes prefix to namespace) a qualified name
786
     *
787
     * @param    string $qname qname
788
     * @return    string expanded qname
789
     * @access   private
790
     */
791
    function expandQname($qname)
792
    {
793
        // get element prefix
794
        if (strpos($qname, ':') && !preg_match('/^http:\/\//', $qname)) {
795
            // get unqualified name
796
            $name = substr(strstr($qname, ':'), 1);
797
            // get ns prefix
798
            $prefix = substr($qname, 0, strpos($qname, ':'));
799
            if (isset($this->namespaces[$prefix])) {
800
                return $this->namespaces[$prefix] . ':' . $name;
801
            } else {
802
                return $qname;
803
            }
804
        } else {
805
            return $qname;
806
        }
807
    }
808
809
    /**
810
     * returns the local part of a prefixed string
811
     * returns the original string, if not prefixed
812
     *
813
     * @param string $str The prefixed string
814
     * @return string The local part
815
     * @access public
816
     */
817
    function getLocalPart($str)
818
    {
819
        if ($sstr = strrchr($str, ':')) {
820
            // get unqualified name
821
            return substr($sstr, 1);
822
        } else {
823
            return $str;
824
        }
825
    }
826
827
    /**
828
     * returns the prefix part of a prefixed string
829
     * returns false, if not prefixed
830
     *
831
     * @param string $str The prefixed string
832
     * @return false|string The prefix or false if there is no prefix
833
     * @access public
834
     */
835
    function getPrefix($str)
836
    {
837
        if ($pos = strrpos($str, ':')) {
838
            // get prefix
839
            return substr($str, 0, $pos);
840
        }
841
        return false;
842
    }
843
844
    /**
845
     * pass it a prefix, it returns a namespace
846
     *
847
     * @param string $prefix The prefix
848
     * @return mixed The namespace, false if no namespace has the specified prefix
849
     * @access public
850
     */
851
    function getNamespaceFromPrefix($prefix)
852
    {
853
        if (isset($this->namespaces[$prefix])) {
854
            return $this->namespaces[$prefix];
855
        }
856
        //$this->setError("No namespace registered for prefix '$prefix'");
857
        return false;
858
    }
859
860
    /**
861
     * returns the prefix for a given namespace (or prefix)
862
     * or false if no prefixes registered for the given namespace
863
     *
864
     * @param string $ns The namespace
865
     * @return false|string The prefix, false if the namespace has no prefixes
866
     * @access public
867
     */
868
    function getPrefixFromNamespace($ns)
869
    {
870
        foreach ($this->namespaces as $p => $n) {
871
            if ($ns == $n || $ns == $p) {
872
                $this->usedNamespaces[$p] = $n;
873
                return $p;
874
            }
875
        }
876
        return false;
877
    }
878
879
    /**
880
     * returns the time in ODBC canonical form with microseconds
881
     *
882
     * @return string The time in ODBC canonical form with microseconds
883
     * @access public
884
     */
885
    function getmicrotime()
886
    {
887
        if (function_exists('gettimeofday')) {
888
            $tod = gettimeofday();
889
            $sec = $tod['sec'];
890
            $usec = $tod['usec'];
891
        } else {
892
            $sec = time();
893
            $usec = 0;
894
        }
895
        $dtx = new DateTime("@$sec");
896
    return
897
          date_format($dtx, 'Y-m-d H:i:s') . '.' . sprintf('%06d', $usec);
898
    }
899
900
    /**
901
     * Returns a string with the output of var_dump
902
     *
903
     * @param mixed $data The variable to var_dump
904
     * @return string The output of var_dump
905
     * @access public
906
     */
907
    function varDump($data)
908
    {
909
        ob_start();
910
        var_dump($data);
911
        $ret_val = ob_get_contents();
912
        ob_end_clean();
913
        return $ret_val;
914
    }
915
916
    /**
917
     * represents the object as a string
918
     *
919
     * @return    string
920
     * @access   public
921
     */
922
    function __toString()
923
    {
924
        return $this->varDump($this);
925
    }
926
}
927
928
// XML Schema Datatype Helper Functions
929
930
//xsd:dateTime helpers
931
932
/**
933
 * convert unix timestamp to ISO 8601 compliant date string
934
 *
935
 * @param    int $timestamp Unix time stamp
936
 * @param    boolean $utc Whether the time stamp is UTC or local
937
 * @return   false|string ISO 8601 date string or false
938
 * @access   public
939
 */
940
function timestamp_to_iso8601($timestamp, $utc = true)
941
{
942
    $datestr = date('Y-m-d\TH:i:sO', $timestamp);
943
    $pos = strrpos($datestr, "+");
944
    if ($pos === false) {
945
        $pos = strrpos($datestr, "-");
946
    }
947
    if ($pos !== false) {
948
        if (strlen($datestr) == $pos + 5) {
949
            $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
950
        }
951
    }
952
    if ($utc) {
953
        $pattern = '/' .
954
            '([0-9]{4})-' . // centuries & years CCYY-
955
            '([0-9]{2})-' . // months MM-
956
            '([0-9]{2})' . // days DD
957
            'T' . // separator T
958
            '([0-9]{2}):' . // hours hh:
959
            '([0-9]{2}):' . // minutes mm:
960
            '([0-9]{2})(\.[0-9]*)?' . // seconds ss.ss...
961
            '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
962
            '/';
963
964
        if (preg_match($pattern, $datestr, $regs)) {
965
            return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
966
        }
967
        return false;
968
    } else {
969
        return $datestr;
970
    }
971
}
972
973
/**
974
 * convert ISO 8601 compliant date string to unix timestamp
975
 *
976
 * @param    string $datestr ISO 8601 compliant date string
977
 * @return   false|int Unix timestamp (int) or false
978
 * @access   public
979
 */
980
function iso8601_to_timestamp($datestr)
981
{
982
    $pattern = '/' .
983
        '([0-9]{4})-' . // centuries & years CCYY-
984
        '([0-9]{2})-' . // months MM-
985
        '([0-9]{2})' . // days DD
986
        'T' . // separator T
987
        '([0-9]{2}):' . // hours hh:
988
        '([0-9]{2}):' . // minutes mm:
989
        '([0-9]{2})(\.[0-9]+)?' . // seconds ss.ss...
990
        '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
991
        '/';
992
    if (preg_match($pattern, $datestr, $regs)) {
993
        // not utc
994
        if ($regs[8] != 'Z') {
995
            $op = substr($regs[8], 0, 1);
996
            $h = substr($regs[8], 1, 2);
997
            $m = substr($regs[8], strlen($regs[8]) - 2, 2);
998
            if ($op == '-') {
999
                $regs[4] = intval($regs[4]) + intval($h);
1000
                $regs[5] = intval($regs[5]) + intval($m);
1001
            } elseif ($op == '+') {
1002
                $regs[4] = intval($regs[4]) - intval($h);
1003
                $regs[5] = intval($regs[5]) - intval($m);
1004
            }
1005
        }
1006
        return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1007
//      return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
1008
    } else {
1009
        return false;
1010
    }
1011
}
1012
1013
/**
1014
 * sleeps some number of microseconds
1015
 *
1016
 * @param    string $usec the number of microseconds to sleep
1017
 * @access   public
1018
 * @deprecated
1019
 */
1020
function usleepWindows($usec)
1021
{
1022
    $start = gettimeofday();
1023
1024
    do {
1025
        $stop = gettimeofday();
1026
        $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
1027
            + $stop['usec'] - $start['usec'];
1028
    } while ($timePassed < $usec);
1029
}
1030
1031
1032
/**
1033
 * Contains information for a SOAP fault.
1034
 * Mainly used for returning faults from deployed functions
1035
 * in a server instance.
1036
 *
1037
 * @author   Dietrich Ayala <[email protected]>
1038
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1039
 * @access public
1040
 */
1041
class nusoap_fault extends nusoap_base
1042
{
1043
    /**
1044
     * The fault code (client|server)
1045
     *
1046
     * @var string
1047
     * @access private
1048
     */
1049
    var $faultcode;
1050
    /**
1051
     * The fault actor
1052
     *
1053
     * @var string
1054
     * @access private
1055
     */
1056
    var $faultactor;
1057
    /**
1058
     * The fault string, a description of the fault
1059
     *
1060
     * @var string
1061
     * @access private
1062
     */
1063
    var $faultstring;
1064
    /**
1065
     * The fault detail, typically a string or array of string
1066
     *
1067
     * @var mixed
1068
     * @access private
1069
     */
1070
    var $faultdetail;
1071
1072
    /**
1073
     * constructor
1074
     *
1075
     * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1076
     * @param string $faultactor only used when msg routed between multiple actors
1077
     * @param string $faultstring human readable error message
1078
     * @param mixed $faultdetail detail, typically a string or array of string
1079
     */
1080
    function __construct($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '')
1081
    {
1082
        parent::__construct();
1083
        $this->faultcode = $faultcode;
1084
        $this->faultactor = $faultactor;
1085
        $this->faultstring = $faultstring;
1086
        $this->faultdetail = $faultdetail;
1087
    }
1088
1089
    /**
1090
     * serialize a fault
1091
     *
1092
     * @return    string    The serialization of the fault instance.
1093
     * @access   public
1094
     */
1095
    function serialize()
1096
    {
1097
        $ns_string = '';
1098
        foreach ($this->namespaces as $k => $v) {
1099
            $ns_string .= "\n  xmlns:$k=\"$v\"";
1100
        }
1101
1102
      return '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?>' .
1103
       '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string . ">\n" .
1104
       '<SOAP-ENV:Body>' .
1105
       '<SOAP-ENV:Fault>' .
1106
       $this->serialize_val($this->faultcode, 'faultcode') .
1107
       $this->serialize_val($this->faultstring, 'faultstring') .
1108
       $this->serialize_val($this->faultactor, 'faultactor') .
1109
       $this->serialize_val($this->faultdetail, 'detail') .
1110
       '</SOAP-ENV:Fault>' .
1111
       '</SOAP-ENV:Body>' .
1112
       '</SOAP-ENV:Envelope>';
1113
    }
1114
}
1115
1116
1117
/**
1118
 * Backward compatibility
1119
 */
1120
class soap_fault extends nusoap_fault
1121
{
1122
}
1123
1124
1125
/**
1126
 * parses an XML Schema, allows access to it's data, other utility methods.
1127
 * imperfect, no validation... yet, but quite functional.
1128
 *
1129
 * @author   Dietrich Ayala <[email protected]>
1130
 * @author   Scott Nichol <[email protected]>
1131
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1132
 * @access   public
1133
 */
1134
class nusoap_xmlschema extends nusoap_base
1135
{
1136
    // files
1137
    var $schema = '';
1138
    var $xml = '';
1139
    // namespaces
1140
    var $enclosingNamespaces;
1141
    // schema info
1142
    var $schemaInfo = array();
1143
    var $schemaTargetNamespace = '';
1144
    // types, elements, attributes defined by the schema
1145
    var $attributes = array();
1146
    var $complexTypes = array();
1147
    var $complexTypeStack = array();
1148
    var $currentComplexType = null;
1149
    var $elements = array();
1150
    var $elementStack = array();
1151
    var $currentElement = null;
1152
    var $simpleTypes = array();
1153
    var $simpleTypeStack = array();
1154
    var $currentSimpleType = null;
1155
    // imports
1156
    var $imports = array();
1157
    // parser vars
1158
    var $parser;
1159
    var $position = 0;
1160
    var $depth = 0;
1161
    var $depth_array = array();
1162
    var $message = array();
1163
    var $defaultNamespace = array();
1164
1165
    /**
1166
     * constructor
1167
     *
1168
     * @param    string $schema schema document URI
1169
     * @param    string $xml xml document URI
1170
     * @param    string $namespaces namespaces defined in enclosing XML
1171
     * @access   public
1172
     */
1173
    function __construct($schema = '', $xml = '', $namespaces = array())
1174
    {
1175
        parent::__construct();
1176
        $this->debug('nusoap_xmlschema class instantiated, inside constructor');
1177
        // files
1178
        $this->schema = $schema;
1179
        $this->xml = $xml;
1180
1181
        // namespaces
1182
        $this->enclosingNamespaces = $namespaces;
1183
        $this->namespaces = array_merge($this->namespaces, $namespaces);
1184
1185
        // parse schema file
1186
        if ($schema != '') {
1187
            $this->debug('initial schema file: ' . $schema);
1188
            $this->parseFile($schema, 'schema');
1189
        }
1190
1191
        // parse xml file
1192
        if ($xml != '') {
1193
            $this->debug('initial xml file: ' . $xml);
1194
            $this->parseFile($xml, 'xml');
1195
        }
1196
    }
1197
1198
    /**
1199
     * parse an XML file
1200
     *
1201
     * @param string $xml path/URL to XML file
1202
     * @param string $type (schema | xml)
1203
     * @return boolean
1204
     * @access public
1205
     */
1206
    function parseFile($xml, $type)
1207
    {
1208
        // parse xml file
1209
        if ($xml != "") {
1210
            $xmlStr = @join("", @file($xml));
1211
            if ($xmlStr == "") {
1212
                $msg = 'Error reading XML from ' . $xml;
1213
                $this->setError($msg);
1214
                $this->debug($msg);
1215
                return false;
1216
            } else {
1217
                $this->debug("parsing $xml");
1218
                $this->parseString($xmlStr, $type);
1219
                $this->debug("done parsing $xml");
1220
                return true;
1221
            }
1222
        }
1223
        return false;
1224
    }
1225
1226
    /**
1227
     * parse an XML string
1228
     *
1229
     * @param    string $xml path or URL
1230
     * @param    string $type (schema|xml)
1231
     * @access   private
1232
     */
1233
    function parseString($xml, $type)
1234
    {
1235
        // parse xml string
1236
        if ($xml != "") {
1237
            // Create an XML parser.
1238
            $this->parser = xml_parser_create();
1239
            // Set the options for parsing the XML data.
1240
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1241
1242
            // Set the object for the parser.
1243
            xml_set_object($this->parser, $this);
1244
1245
            // Set the element handlers for the parser.
1246
            if ($type == "schema") {
1247
                xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
1248
                xml_set_character_data_handler($this->parser, 'schemaCharacterData');
1249
            } elseif ($type == "xml") {
1250
                xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
1251
                xml_set_character_data_handler($this->parser, 'xmlCharacterData');
1252
            }
1253
1254
            libxml_disable_entity_loader(true); // Avoid load of external entities (security problem). Required only for libxml < 2.
1255
1256
            // Parse the XML file.
1257
            if (!xml_parse($this->parser, $xml, true)) {
1258
                // Display an error message.
1259
                $errstr = sprintf(
1260
                    'XML error parsing XML schema on line %d: %s',
1261
                    xml_get_current_line_number($this->parser),
1262
                    xml_error_string(xml_get_error_code($this->parser))
1263
                );
1264
                $this->debug($errstr);
1265
                $this->debug("XML payload:\n" . $xml);
1266
                $this->setError($errstr);
1267
            }
1268
1269
            xml_parser_free($this->parser);
1270
            unset($this->parser);
1271
        } else {
1272
            $this->debug('no xml passed to parseString()!!');
1273
            $this->setError('no xml passed to parseString()!!');
1274
        }
1275
    }
1276
1277
    /**
1278
     * gets a type name for an unnamed type
1279
     *
1280
     * @param    string $ename Element name
1281
     * @return    string    A type name for an unnamed type
1282
     * @access    private
1283
     */
1284
    function CreateTypeName($ename)
1285
    {
1286
        $scope = '';
1287
        for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1288
            $scope .= $this->complexTypeStack[$i] . '_';
1289
        }
1290
        return $scope . $ename . '_ContainedType';
1291
    }
1292
1293
    /**
1294
     * start-element handler
1295
     *
1296
     * @param    string $parser XML parser object
1297
     * @param    string $name element name
1298
     * @param    array $attrs associative array of attributes
1299
     * @access   private
1300
     */
1301
    function schemaStartElement($parser, $name, $attrs)
1302
    {
1303
1304
        // position in the total number of elements, starting from 0
1305
        $pos = $this->position++;
1306
        $depth = $this->depth++;
1307
        // set self as current value for this depth
1308
        $this->depth_array[$depth] = $pos;
1309
        $this->message[$pos] = array('cdata' => '');
1310
        if ($depth > 0) {
1311
            $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1312
        } else {
1313
            $this->defaultNamespace[$pos] = false;
1314
        }
1315
1316
        // get element prefix
1317
        if ($prefix = $this->getPrefix($name)) {
1318
            // get unqualified name
1319
            $name = $this->getLocalPart($name);
1320
        } else {
1321
            $prefix = '';
1322
        }
1323
1324
        // loop thru attributes, expanding, and registering namespace declarations
1325
        if (count($attrs) > 0) {
1326
            foreach ($attrs as $k => $v) {
1327
                // if ns declarations, add to class level array of valid namespaces
1328
                if (preg_match('/^xmlns/', $k)) {
1329
                    //$this->xdebug("$k: $v");
1330
                    //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1331
                    if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
1332
                        //$this->xdebug("Add namespace[$ns_prefix] = $v");
1333
                        $this->namespaces[$ns_prefix] = $v;
1334
                    } else {
1335
                        $this->defaultNamespace[$pos] = $v;
1336
                        if (!$this->getPrefixFromNamespace($v)) {
1337
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
1338
                        }
1339
                    }
1340
                    if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
1341
                        $this->XMLSchemaVersion = $v;
1342
                        $this->namespaces['xsi'] = $v . '-instance';
1343
                    }
1344
                }
1345
            }
1346
            $eAttrs = array ();
1347
            foreach ($attrs as $k => $v) {
1348
                // expand each attribute
1349
                $k = strpos($k, ':') ? $this->expandQname($k) : $k;
1350
                $v = strpos($v, ':') ? $this->expandQname($v) : $v;
1351
                $eAttrs[$k] = $v;
1352
            }
1353
            $attrs = $eAttrs;
1354
        } else {
1355
            $attrs = array();
1356
        }
1357
        // find status, register data
1358
        switch ($name) {
1359
            case 'all':            // (optional) compositor content for a complexType
1360
            case 'choice':
1361
            case 'group':
1362
            case 'sequence':
1363
                //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1364
                $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1365
                //if($name == 'all' || $name == 'sequence'){
1366
                //  $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1367
                //}
1368
                break;
1369
            case 'attribute':    // complexType attribute
1370
                //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1371
                $this->xdebug("parsing attribute:");
1372
                $this->appendDebug($this->varDump($attrs));
1373
                if (!isset($attrs['form'])) {
1374
                    // TODO: handle globals
1375
                    $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1376
                }
1377
                if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1378
                    $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1379
                    if (!strpos($v, ':')) {
1380
                        // no namespace in arrayType attribute value...
1381
                        if ($this->defaultNamespace[$pos]) {
1382
                            // ...so use the default
1383
                            $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1384
                        }
1385
                    }
1386
                }
1387
                if (isset($attrs['name'])) {
1388
                    $this->attributes[$attrs['name']] = $attrs;
1389
                    $aname = $attrs['name'];
1390
                } elseif (isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType') {
1391
                    if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1392
                        $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1393
                    } else {
1394
                        $aname = '';
1395
                    }
1396
                } elseif (isset($attrs['ref'])) {
1397
                    $aname = $attrs['ref'];
1398
                    $this->attributes[$attrs['ref']] = $attrs;
1399
                } else {
1400
                    $aname = '';
1401
                }
1402
1403
                if ($this->currentComplexType) {    // This should *always* be
1404
                    $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1405
                }
1406
                // arrayType attribute
1407
                if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType') {
1408
                    $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1409
                    if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1410
                        $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1411
                    } else {
1412
                        $v = '';
1413
                    }
1414
                    if (strpos($v, '[,]')) {
1415
                        $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1416
                    }
1417
                    $v = substr($v, 0, strpos($v, '[')); // clip the []
1418
                    if (!strpos($v, ':') && isset($this->typemap[$this->XMLSchemaVersion][$v])) {
1419
                        $v = $this->XMLSchemaVersion . ':' . $v;
1420
                    }
1421
                    $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1422
                }
1423
                break;
1424
            case 'complexContent':    // (optional) content for a complexType
1425
                $this->xdebug("do nothing for element $name");
1426
                break;
1427
            case 'complexType':
1428
                $this->complexTypeStack[] = $this->currentComplexType;
1429
                if (isset($attrs['name'])) {
1430
                    // TODO: what is the scope of named complexTypes that appear
1431
                    //       nested within other c complexTypes?
1432
                    $this->xdebug('processing named complexType ' . $attrs['name']);
1433
                    //$this->currentElement = false;
1434
                    $this->currentComplexType = $attrs['name'];
1435
                } else {
1436
                    $name = $this->CreateTypeName($this->currentElement);
1437
                    $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1438
                    $this->currentComplexType = $name;
1439
                    //$this->currentElement = false;
1440
                }
1441
                $this->complexTypes[$this->currentComplexType] = $attrs;
1442
                $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1443
                // This is for constructs like
1444
                //           <complexType name="ListOfString" base="soap:Array">
1445
                //                <sequence>
1446
                //                    <element name="string" type="xsd:string"
1447
                //                        minOccurs="0" maxOccurs="unbounded" />
1448
                //                </sequence>
1449
                //            </complexType>
1450
                if (isset($attrs['base']) && preg_match('/:Array$/', $attrs['base'])) {
1451
                    $this->xdebug('complexType is unusual array');
1452
                    $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1453
                } else {
1454
                    $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1455
                }
1456
                $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
1457
                break;
1458
            case 'element':
1459
                $this->elementStack[] = $this->currentElement;
1460
                if (!isset($attrs['form'])) {
1461
                    if ($this->currentComplexType) {
1462
                        $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1463
                    } else {
1464
                        // global
1465
                        $attrs['form'] = 'qualified';
1466
                    }
1467
                }
1468
                if (isset($attrs['type'])) {
1469
                    $this->xdebug("processing typed element " . $attrs['name'] . " of type " . $attrs['type']);
1470
                    if (!$this->getPrefix($attrs['type'])) {
1471
                        if ($this->defaultNamespace[$pos]) {
1472
                            $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1473
                            $this->xdebug('used default namespace to make type ' . $attrs['type']);
1474
                        }
1475
                    }
1476
                    // This is for constructs like
1477
                    //           <complexType name="ListOfString" base="soap:Array">
1478
                    //                <sequence>
1479
                    //                    <element name="string" type="xsd:string"
1480
                    //                        minOccurs="0" maxOccurs="unbounded" />
1481
                    //                </sequence>
1482
                    //            </complexType>
1483
                    if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1484
                        $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1485
                        $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1486
                    }
1487
                    $this->currentElement = $attrs['name'];
1488
                    $ename = $attrs['name'];
1489
                } elseif (isset($attrs['ref'])) {
1490
                    $this->xdebug("processing element as ref to " . $attrs['ref']);
1491
                    $this->currentElement = "ref to " . $attrs['ref'];
1492
                    $ename = $this->getLocalPart($attrs['ref']);
1493
                } else {
1494
                    $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1495
                    $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1496
                    $this->currentElement = $attrs['name'];
1497
                    $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1498
                    $ename = $attrs['name'];
1499
                }
1500
                if (isset($ename) && $this->currentComplexType) {
1501
                    $this->xdebug("add element $ename to complexType $this->currentComplexType");
1502
                    $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1503
                } elseif (!isset($attrs['ref'])) {
1504
                    $this->xdebug("add element $ename to elements array");
1505
                    $this->elements[$attrs['name']] = $attrs;
1506
                    $this->elements[$attrs['name']]['typeClass'] = 'element';
1507
                }
1508
                break;
1509
            case 'enumeration':    //   restriction value list member
1510
                $this->xdebug('enumeration ' . $attrs['value']);
1511
                if ($this->currentSimpleType) {
1512
                    $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1513
                } elseif ($this->currentComplexType) {
1514
                    $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1515
                }
1516
                break;
1517
            case 'extension':    // simpleContent or complexContent type extension
1518
                $this->xdebug('extension ' . $attrs['base']);
1519
                if ($this->currentComplexType) {
1520
                    $ns = $this->getPrefix($attrs['base']);
1521
                    if ($ns == '') {
1522
                        $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
1523
                    } else {
1524
                        $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1525
                    }
1526
                } else {
1527
                    $this->xdebug('no current complexType to set extensionBase');
1528
                }
1529
                break;
1530
            case 'import':
1531
                if (isset($attrs['schemaLocation'])) {
1532
                    $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1533
                    $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1534
                } else {
1535
                    $this->xdebug('import namespace ' . $attrs['namespace']);
1536
                    $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1537
                    if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
1538
                        $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
1539
                    }
1540
                }
1541
                break;
1542
            case 'include':
1543
                if (isset($attrs['schemaLocation'])) {
1544
                    $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
1545
                    $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1546
                } else {
1547
                    $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
1548
                }
1549
                break;
1550
            case 'list':    // simpleType value list
1551
                $this->xdebug("do nothing for element $name");
1552
                break;
1553
            case 'restriction':    // simpleType, simpleContent or complexContent value restriction
1554
                $this->xdebug('restriction ' . $attrs['base']);
1555
                if ($this->currentSimpleType) {
1556
                    $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1557
                } elseif ($this->currentComplexType) {
1558
                    $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1559
                    if (strstr($attrs['base'], ':') == ':Array') {
1560
                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1561
                    }
1562
                }
1563
                break;
1564
            case 'schema':
1565
                $this->schemaInfo = $attrs;
1566
                $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1567
                if (isset($attrs['targetNamespace'])) {
1568
                    $this->schemaTargetNamespace = $attrs['targetNamespace'];
1569
                }
1570
                if (!isset($attrs['elementFormDefault'])) {
1571
                    $this->schemaInfo['elementFormDefault'] = 'unqualified';
1572
                }
1573
                if (!isset($attrs['attributeFormDefault'])) {
1574
                    $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1575
                }
1576
                break;
1577
            case 'simpleContent':    // (optional) content for a complexType
1578
                if ($this->currentComplexType) {    // This should *always* be
1579
                    $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
1580
                } else {
1581
                    $this->xdebug("do nothing for element $name because there is no current complexType");
1582
                }
1583
                break;
1584
            case 'simpleType':
1585
                $this->simpleTypeStack[] = $this->currentSimpleType;
1586
                if (isset($attrs['name'])) {
1587
                    $this->xdebug("processing simpleType for name " . $attrs['name']);
1588
                    $this->currentSimpleType = $attrs['name'];
1589
                    $this->simpleTypes[$attrs['name']] = $attrs;
1590
                    $this->simpleTypes[$attrs['name']]['typeClass'] = 'simpleType';
1591
                    $this->simpleTypes[$attrs['name']]['phpType'] = 'scalar';
1592
                } else {
1593
                    $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1594
                    $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1595
                    $this->currentSimpleType = $name;
1596
                    //$this->currentElement = false;
1597
                    $this->simpleTypes[$this->currentSimpleType] = $attrs;
1598
                    $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1599
                }
1600
                break;
1601
            case 'union':    // simpleType type list
1602
                $this->xdebug("do nothing for element $name");
1603
                break;
1604
            default:
1605
                $this->xdebug("do not have any logic to process element $name");
1606
        }
1607
    }
1608
1609
    /**
1610
     * end-element handler
1611
     *
1612
     * @param    string $parser XML parser object
1613
     * @param    string $name element name
1614
     * @access   private
1615
     */
1616
    function schemaEndElement($parser, $name)
1617
    {
1618
        // bring depth down a notch
1619
        $this->depth--;
1620
        // get element prefix
1621
        if ($this->getPrefix($name)) {
1622
            // get unqualified name
1623
            $name = $this->getLocalPart($name);
1624
        }
1625
        // move on...
1626
        if ($name == 'complexType') {
1627
            $this->xdebug('done processing complexType ' . ($this->currentComplexType ?: '(unknown)'));
1628
            $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1629
            $this->currentComplexType = array_pop($this->complexTypeStack);
1630
            //$this->currentElement = false;
1631
        }
1632
        if ($name == 'element') {
1633
            $this->xdebug('done processing element ' . ($this->currentElement ?: '(unknown)'));
1634
            $this->currentElement = array_pop($this->elementStack);
1635
        }
1636
        if ($name == 'simpleType') {
1637
            $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ?: '(unknown)'));
1638
            $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1639
            $this->currentSimpleType = array_pop($this->simpleTypeStack);
1640
        }
1641
    }
1642
1643
    /**
1644
     * element content handler
1645
     *
1646
     * @param    string $parser XML parser object
1647
     * @param    string $data element content
1648
     * @access   private
1649
     */
1650
    function schemaCharacterData($parser, $data)
1651
    {
1652
        $pos = $this->depth_array[$this->depth - 1];
1653
        $this->message[$pos]['cdata'] .= $data;
1654
    }
1655
1656
    /**
1657
     * serialize the schema
1658
     *
1659
     * @access   public
1660
     */
1661
    function serializeSchema()
1662
    {
1663
1664
        $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1665
        $xml = '';
1666
        // imports
1667
        if (sizeof($this->imports) > 0) {
1668
            foreach ($this->imports as $ns => $list) {
1669
                foreach ($list as $ii) {
1670
                    if ($ii['location'] != '') {
1671
                        $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1672
                    } else {
1673
                        $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1674
                    }
1675
                }
1676
            }
1677
        }
1678
        // complex types
1679
        foreach ($this->complexTypes as $typeName => $attrs) {
1680
            $contentStr = '';
1681
            // serialize child elements
1682
            if (isset($attrs['elements']) && (count($attrs['elements']) > 0)) {
1683
                foreach ($attrs['elements'] as $element => $eParts) {
1684
                    if (isset($eParts['ref'])) {
1685
                        $contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1686
                    } else {
1687
                        $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1688
                        foreach ($eParts as $aName => $aValue) {
1689
                            // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1690
                            if ($aName != 'name' && $aName != 'type') {
1691
                                $contentStr .= " $aName=\"$aValue\"";
1692
                            }
1693
                        }
1694
                        $contentStr .= "/>\n";
1695
                    }
1696
                }
1697
                // compositor wraps elements
1698
                if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1699
                    $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n" . $contentStr . "  </$schemaPrefix:$attrs[compositor]>\n";
1700
                }
1701
            }
1702
            // attributes
1703
            if (isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)) {
1704
                foreach ($attrs['attrs'] as $aParts) {
1705
                    $contentStr .= "    <$schemaPrefix:attribute";
1706
                    foreach ($aParts as $a => $v) {
1707
                        if ($a == 'ref' || $a == 'type') {
1708
                            $contentStr .= " $a=\"" . $this->contractQName($v) . '"';
1709
                        } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1710
                            $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1711
                            $contentStr .= ' wsdl:arrayType="' . $this->contractQName($v) . '"';
1712
                        } else {
1713
                            $contentStr .= " $a=\"$v\"";
1714
                        }
1715
                    }
1716
                    $contentStr .= "/>\n";
1717
                }
1718
            }
1719
            // if restriction
1720
            if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != '') {
1721
                $contentStr = "   <$schemaPrefix:restriction base=\"" . $this->contractQName($attrs['restrictionBase']) . "\">\n" . $contentStr . "   </$schemaPrefix:restriction>\n";
1722
                // complex or simple content
1723
                if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)) {
1724
                    $contentStr = "  <$schemaPrefix:complexContent>\n" . $contentStr . "  </$schemaPrefix:complexContent>\n";
1725
                }
1726
            }
1727
            // finalize complex type
1728
            if ($contentStr != '') {
1729
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n" . $contentStr . " </$schemaPrefix:complexType>\n";
1730
            } else {
1731
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1732
            }
1733
            $xml .= $contentStr;
1734
        }
1735
        // simple types
1736
        if (isset($this->simpleTypes) && count($this->simpleTypes) > 0) {
1737
            foreach ($this->simpleTypes as $typeName => $eParts) {
1738
                $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"" . $this->contractQName($eParts['type']) . "\">\n";
1739
                if (isset($eParts['enumeration'])) {
1740
                    foreach ($eParts['enumeration'] as $e) {
1741
                        $xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1742
                    }
1743
                }
1744
                $xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1745
            }
1746
        }
1747
        // elements
1748
        if (isset($this->elements) && count($this->elements) > 0) {
1749
            foreach ($this->elements as $element => $eParts) {
1750
                $xml .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n";
1751
            }
1752
        }
1753
        // attributes
1754
        if (isset($this->attributes) && count($this->attributes) > 0) {
1755
            foreach ($this->attributes as $attr => $aParts) {
1756
                $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"" . $this->contractQName($aParts['type']) . "\"\n/>";
1757
            }
1758
        }
1759
        // finish 'er up
1760
        $attr = '';
1761
        foreach ($this->schemaInfo as $k => $v) {
1762
            if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1763
                $attr .= " $k=\"$v\"";
1764
            }
1765
        }
1766
        $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1767
        foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1768
            $el .= " xmlns:$nsp=\"$ns\"";
1769
        }
1770
1771
      return $el . ">\n" . $xml . "</$schemaPrefix:schema>\n";
1772
    }
1773
1774
    /**
1775
     * adds debug data to the clas level debug string
1776
     *
1777
     * @param    string $string debug data
1778
     * @access   private
1779
     */
1780
    function xdebug($string)
1781
    {
1782
        $this->debug('<' . $this->schemaTargetNamespace . '> ' . $string);
1783
    }
1784
1785
    /**
1786
     * get the PHP type of a user defined type in the schema
1787
     * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1788
     * returns false if no type exists, or not w/ the given namespace
1789
     * else returns a string that is either a native php type, or 'struct'
1790
     *
1791
     * @param string $type name of defined type
1792
     * @param string $ns namespace of type
1793
     * @return mixed
1794
     * @access public
1795
     * @deprecated
1796
     */
1797
    function getPHPType($type, $ns)
1798
    {
1799
        if (isset($this->typemap[$ns][$type])) {
1800
            //print "found type '$type' and ns $ns in typemap<br>";
1801
            return $this->typemap[$ns][$type];
1802
        } elseif (isset($this->complexTypes[$type])) {
1803
            //print "getting type '$type' and ns $ns from complexTypes array<br>";
1804
            return $this->complexTypes[$type]['phpType'];
1805
        }
1806
        return false;
1807
    }
1808
1809
    /**
1810
     * returns an associative array of information about a given type
1811
     * returns false if no type exists by the given name
1812
     *
1813
     *    For a complexType typeDef = array(
1814
     *    'restrictionBase' => '',
1815
     *    'phpType' => '',
1816
     *    'compositor' => '(sequence|all)',
1817
     *    'elements' => array(), // refs to elements array
1818
     *    'attrs' => array() // refs to attributes array
1819
     *    ... and so on (see addComplexType)
1820
     *    )
1821
     *
1822
     *   For simpleType or element, the array has different keys.
1823
     *
1824
     * @param string $type
1825
     * @return mixed
1826
     * @access public
1827
     * @see addComplexType
1828
     * @see addSimpleType
1829
     * @see addElement
1830
     */
1831
    function getTypeDef($type)
1832
    {
1833
        //$this->debug("in getTypeDef for type $type");
1834
        if (substr($type, -1) == '^') {
1835
            $is_element = 1;
1836
            $type = substr($type, 0, -1);
1837
        } else {
1838
            $is_element = 0;
1839
        }
1840
1841
        if ((!$is_element) && isset($this->complexTypes[$type])) {
1842
            $this->xdebug("in getTypeDef, found complexType $type");
1843
            return $this->complexTypes[$type];
1844
        } elseif ((!$is_element) && isset($this->simpleTypes[$type])) {
1845
            $this->xdebug("in getTypeDef, found simpleType $type");
1846
            if (!isset($this->simpleTypes[$type]['phpType'])) {
1847
                // get info for type to tack onto the simple type
1848
                // TODO: can this ever really apply (i.e. what is a simpleType really?)
1849
                $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1850
                $etype = $this->getTypeDef($uqType);
1851
                if ($etype) {
1852
                    $this->xdebug("in getTypeDef, found type for simpleType $type:");
1853
                    $this->xdebug($this->varDump($etype));
1854
                    if (isset($etype['phpType'])) {
1855
                        $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1856
                    }
1857
                    if (isset($etype['elements'])) {
1858
                        $this->simpleTypes[$type]['elements'] = $etype['elements'];
1859
                    }
1860
                }
1861
            }
1862
            return $this->simpleTypes[$type];
1863
        } elseif (isset($this->elements[$type])) {
1864
            $this->xdebug("in getTypeDef, found element $type");
1865
            if (!isset($this->elements[$type]['phpType'])) {
1866
                // get info for type to tack onto the element
1867
                $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1868
                $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1869
                $etype = $this->getTypeDef($uqType);
1870
                if ($etype) {
1871
                    $this->xdebug("in getTypeDef, found type for element $type:");
1872
                    $this->xdebug($this->varDump($etype));
1873
                    if (isset($etype['phpType'])) {
1874
                        $this->elements[$type]['phpType'] = $etype['phpType'];
1875
                    }
1876
                    if (isset($etype['elements'])) {
1877
                        $this->elements[$type]['elements'] = $etype['elements'];
1878
                    }
1879
                    if (isset($etype['extensionBase'])) {
1880
                        $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
1881
                    }
1882
                } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1883
                    $this->xdebug("in getTypeDef, element $type is an XSD type");
1884
                    $this->elements[$type]['phpType'] = 'scalar';
1885
                }
1886
            }
1887
            return $this->elements[$type];
1888
        } elseif (isset($this->attributes[$type])) {
1889
            $this->xdebug("in getTypeDef, found attribute $type");
1890
            return $this->attributes[$type];
1891
        } elseif (preg_match('/_ContainedType$/', $type)) {
1892
            $this->xdebug("in getTypeDef, have an untyped element $type");
1893
            $typeDef['typeClass'] = 'simpleType';
1894
            $typeDef['phpType'] = 'scalar';
1895
            $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1896
            return $typeDef;
1897
        }
1898
        $this->xdebug("in getTypeDef, did not find $type");
1899
        return false;
1900
    }
1901
1902
    /**
1903
     * returns a sample serialization of a given type, or false if no type by the given name
1904
     *
1905
     * @param string $type name of type
1906
     * @return false|string
1907
     * @access public
1908
     */
1909
    function serializeTypeDef($type)
1910
    {
1911
        $str = '';
1912
        //print "in sTD() for type $type<br>";
1913
        if ($typeDef = $this->getTypeDef($type)) {
1914
            $str .= '<' . $type;
1915
            if (is_array($typeDef['attrs'])) {
1916
                foreach ($typeDef['attrs'] as $attName => $data) {
1917
                    $str .= " $attName=\"{type = " . $data['type'] . "}\"";
1918
                }
1919
            }
1920
            $str .= " xmlns=\"" . $this->schema['targetNamespace'] . "\"";
1921
            if (count($typeDef['elements']) > 0) {
1922
                $str .= ">";
1923
                foreach ($typeDef['elements'] as $element => $eData) {
1924
                    $str .= $this->serializeTypeDef($element);
1925
                }
1926
                $str .= "</$type>";
1927
            } elseif ($typeDef['typeClass'] == 'element') {
1928
                $str .= "></$type>";
1929
            } else {
1930
                $str .= "/>";
1931
            }
1932
            return $str;
1933
        }
1934
        return false;
1935
    }
1936
1937
    /**
1938
     * returns HTML form elements that allow a user
1939
     * to enter values for creating an instance of the given type.
1940
     *
1941
     * @param string $name name for type instance
1942
     * @param string $type name of type
1943
     * @return string
1944
     * @access public
1945
     * @deprecated
1946
     */
1947
    function typeToForm($name, $type)
1948
    {
1949
        $buffer = '';
1950
        // get typedef
1951
        if ($typeDef = $this->getTypeDef($type)) {
1952
            // if struct
1953
            if ($typeDef['phpType'] == 'struct') {
1954
                $buffer .= '<table>';
1955
                foreach ($typeDef['elements'] as $childDef) {
1956
                    $buffer .= "
1957
					<tr><td align='right'>$childDef[name] (type: " . $this->getLocalPart($childDef['type']) . "):</td>
1958
					<td><input type='text' name='parameters[" . $name . "][$childDef[name]]'></td></tr>";
1959
                }
1960
                $buffer .= '</table>';
1961
                // if array
1962
            } elseif ($typeDef['phpType'] == 'array') {
1963
                $buffer .= '<table>';
1964
              $buffer .= str_repeat("
1965
					<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1966
					<td><input type='text' name='parameters[" . $name . "][]'></td></tr>", 3);
1967
                $buffer .= '</table>';
1968
                // if scalar
1969
            } else {
1970
                $buffer .= "<input type='text' name='parameters[$name]'>";
1971
            }
1972
        } else {
1973
            $buffer .= "<input type='text' name='parameters[$name]'>";
1974
        }
1975
        return $buffer;
1976
    }
1977
1978
    /**
1979
     * adds a complex type to the schema
1980
     * example: array
1981
     * addType(
1982
     *    'ArrayOfstring',
1983
     *    'complexType',
1984
     *    'array',
1985
     *    '',
1986
     *    'SOAP-ENC:Array',
1987
     *    array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1988
     *    'xsd:string'
1989
     * );
1990
     * example: PHP associative array ( SOAP Struct )
1991
     * addType(
1992
     *    'SOAPStruct',
1993
     *    'complexType',
1994
     *    'struct',
1995
     *    'all',
1996
     *    array('myVar'=> array('name'=>'myVar','type'=>'string')
1997
     * );
1998
     *
1999
     * @param string $name
2000
     * @param string $typeClass (complexType|simpleType|attribute)
2001
     * @param string $phpType : currently supported are array and struct (php assoc array)
2002
     * @param string $compositor (all|sequence|choice)
2003
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2004
     * @param array $elements = array ( name = array(name=>'',type=>'') )
2005
     * @param array $attrs = array(
2006
     *    array(
2007
     *        'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
2008
     *        "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
2009
     *    )
2010
     * )
2011
     * @param array $arrayType : namespace:name (http://www.w3.org/2001/XMLSchema:string)
2012
     *
2013
     * @access public
2014
     * @see getTypeDef
2015
     */
2016
    function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = array(), $attrs = array(), $arrayType = '')
2017
    {
2018
        $this->complexTypes[$name] = array(
2019
            'name' => $name,
2020
            'typeClass' => $typeClass,
2021
            'phpType' => $phpType,
2022
            'compositor' => $compositor,
2023
            'restrictionBase' => $restrictionBase,
2024
            'elements' => $elements,
2025
            'attrs' => $attrs,
2026
            'arrayType' => $arrayType
2027
        );
2028
2029
        $this->xdebug("addComplexType $name:");
2030
        $this->appendDebug($this->varDump($this->complexTypes[$name]));
2031
    }
2032
2033
    /**
2034
     * adds a simple type to the schema
2035
     *
2036
     * @param string $name
2037
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2038
     * @param string $typeClass (should always be simpleType)
2039
     * @param string $phpType (should always be scalar)
2040
     * @param array $enumeration array of values
2041
     * @access public
2042
     * @see nusoap_xmlschema
2043
     * @see getTypeDef
2044
     */
2045
    function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = array())
2046
    {
2047
        $this->simpleTypes[$name] = array(
2048
            'name' => $name,
2049
            'typeClass' => $typeClass,
2050
            'phpType' => $phpType,
2051
            'type' => $restrictionBase,
2052
            'enumeration' => $enumeration
2053
        );
2054
2055
        $this->xdebug("addSimpleType $name:");
2056
        $this->appendDebug($this->varDump($this->simpleTypes[$name]));
2057
    }
2058
2059
    /**
2060
     * adds an element to the schema
2061
     *
2062
     * @param array $attrs attributes that must include name and type
2063
     * @see nusoap_xmlschema
2064
     * @access public
2065
     */
2066
    function addElement($attrs)
2067
    {
2068
        if (!$this->getPrefix($attrs['type'])) {
2069
            $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
2070
        }
2071
        $this->elements[$attrs['name']] = $attrs;
2072
        $this->elements[$attrs['name']]['typeClass'] = 'element';
2073
2074
        $this->xdebug("addElement " . $attrs['name']);
2075
        $this->appendDebug($this->varDump($this->elements[$attrs['name']]));
2076
    }
2077
}
2078
2079
/**
2080
 * Backward compatibility
2081
 */
2082
class XMLSchema extends nusoap_xmlschema
2083
{
2084
}
2085
2086
2087
/**
2088
 * For creating serializable abstractions of native PHP types.  This class
2089
 * allows element name/namespace, XSD type, and XML attributes to be
2090
 * associated with a value.  This is extremely useful when WSDL is not
2091
 * used, but is also useful when WSDL is used with polymorphic types, including
2092
 * xsd:anyType and user-defined types.
2093
 *
2094
 * @author   Dietrich Ayala <[email protected]>
2095
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2096
 * @access   public
2097
 */
2098
class soapval extends nusoap_base
2099
{
2100
    /**
2101
     * The XML element name
2102
     *
2103
     * @var string
2104
     * @access private
2105
     */
2106
    var $name;
2107
    /**
2108
     * The XML type name (string or false)
2109
     *
2110
     * @var mixed
2111
     * @access private
2112
     */
2113
    var $type;
2114
    /**
2115
     * The PHP value
2116
     *
2117
     * @var mixed
2118
     * @access private
2119
     */
2120
    var $value;
2121
    /**
2122
     * The XML element namespace (string or false)
2123
     *
2124
     * @var mixed
2125
     * @access private
2126
     */
2127
    var $element_ns;
2128
    /**
2129
     * The XML type namespace (string or false)
2130
     *
2131
     * @var mixed
2132
     * @access private
2133
     */
2134
    var $type_ns;
2135
    /**
2136
     * The XML element attributes (array or false)
2137
     *
2138
     * @var mixed
2139
     * @access private
2140
     */
2141
    var $attributes;
2142
2143
    /** @var false|resource */
2144
    var $fp;
2145
2146
    /**
2147
     * constructor
2148
     *
2149
     * @param    string $name optional name
2150
     * @param    mixed $type optional type name
2151
     * @param    mixed $value optional value
2152
     * @param    mixed $element_ns optional namespace of value
2153
     * @param    mixed $type_ns optional namespace of type
2154
     * @param    mixed $attributes associative array of attributes to add to element serialization
2155
     * @access   public
2156
     */
2157
    function __construct($name = 'soapval', $type = false, $value = -1, $element_ns = false, $type_ns = false, $attributes = false)
2158
    {
2159
        parent::__construct();
2160
        $this->name = $name;
2161
        $this->type = $type;
2162
        $this->value = $value;
2163
        $this->element_ns = $element_ns;
2164
        $this->type_ns = $type_ns;
2165
        $this->attributes = $attributes;
2166
    }
2167
2168
    /**
2169
     * return serialized value
2170
     *
2171
     * @param    string $use The WSDL use value (encoded|literal)
2172
     * @return    string XML data
2173
     * @access   public
2174
     */
2175
    function serialize($use = 'encoded')
2176
    {
2177
        return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2178
    }
2179
2180
    /**
2181
     * decodes a soapval object into a PHP native type
2182
     *
2183
     * @return    mixed
2184
     * @access   public
2185
     */
2186
    function decode()
2187
    {
2188
        return $this->value;
2189
    }
2190
}
2191
2192
2193
/**
2194
 * transport class for sending/receiving data via HTTP and HTTPS
2195
 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2196
 *
2197
 * @author   Dietrich Ayala <[email protected]>
2198
 * @author   Scott Nichol <[email protected]>
2199
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2200
 * @access public
2201
 */
2202
class soap_transport_http extends nusoap_base
2203
{
2204
    var $query = '';
2205
    var $tryagain = false;
2206
    var $url = '';
2207
    var $uri = '';
2208
    var $digest_uri = '';
2209
    var $scheme = '';
2210
    var $host = '';
2211
    var $port = '';
2212
    var $path = '';
2213
    var $request_method = 'POST';
2214
    var $protocol_version = '1.0';
2215
    var $encoding = '';
2216
    var $outgoing_headers = array();
2217
    var $incoming_headers = array();
2218
    var $incoming_cookies = array();
2219
    var $outgoing_payload = '';
2220
    var $incoming_payload = '';
2221
    var $response_status_line;    // HTTP response status line
2222
    var $useSOAPAction = true;
2223
    var $persistentConnection = false;
2224
    var $ch = false;    // cURL handle
2225
    var $ch_options = array();    // cURL custom options
2226
    var $use_curl = false;        // force cURL use
2227
    var $proxy = null;            // proxy information (associative array)
2228
    var $username = '';
2229
    var $password = '';
2230
    var $authtype = '';
2231
    var $digestRequest = array();
2232
    var $certRequest = array();    // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2233
    // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2234
    // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2235
    // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2236
    // passphrase: SSL key password/passphrase
2237
    // certpassword: SSL certificate password
2238
    // verifypeer: default is 1
2239
    // verifyhost: default is 1
2240
2241
    /** @var false|resource */
2242
    var $fp;
2243
    var $errno;
2244
2245
    /**
2246
     * constructor
2247
     *
2248
     * @param string $url The URL to which to connect
2249
     * @param array $curl_options User-specified cURL options
2250
     * @param boolean $use_curl Whether to try to force cURL use
2251
     * @access public
2252
     */
2253
    function __construct($url, $curl_options = null, $use_curl = false)
2254
    {
2255
        parent::__construct();
2256
        $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2257
        $this->appendDebug($this->varDump($curl_options));
2258
        $this->setURL($url);
2259
        if (is_array($curl_options)) {
2260
            $this->ch_options = $curl_options;
2261
        }
2262
        $this->use_curl = $use_curl;
2263
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
2264
        $this->setHeader('User-Agent', $this->title . '/' . $this->version . ' (' . $rev[1] . ')');
2265
    }
2266
2267
    /**
2268
     * sets a cURL option
2269
     *
2270
     * @param    mixed $option The cURL option (always integer?)
2271
     * @param    mixed $value The cURL option value
2272
     * @access   private
2273
     */
2274
    function setCurlOption($option, $value)
2275
    {
2276
        $this->debug("setCurlOption option=$option, value=");
2277
        $this->appendDebug($this->varDump($value));
2278
        curl_setopt($this->ch, $option, $value);
2279
    }
2280
2281
    /**
2282
     * sets an HTTP header
2283
     *
2284
     * @param string $name The name of the header
2285
     * @param string $value The value of the header
2286
     * @access private
2287
     */
2288
    function setHeader($name, $value)
2289
    {
2290
        $this->outgoing_headers[$name] = $value;
2291
        $this->debug("set header $name: $value");
2292
    }
2293
2294
    /**
2295
     * unsets an HTTP header
2296
     *
2297
     * @param string $name The name of the header
2298
     * @access private
2299
     */
2300
    function unsetHeader($name)
2301
    {
2302
        if (isset($this->outgoing_headers[$name])) {
2303
            $this->debug("unset header $name");
2304
            unset($this->outgoing_headers[$name]);
2305
        }
2306
    }
2307
2308
    /**
2309
     * sets the URL to which to connect
2310
     *
2311
     * @param string $url The URL to which to connect
2312
     * @access private
2313
     */
2314
    function setURL($url)
2315
    {
2316
        $this->url = $url;
2317
2318
        $u = parse_url($url);
2319
        foreach ($u as $k => $v) {
2320
            $this->debug("parsed URL $k = $v");
2321
            $this->$k = $v;
2322
        }
2323
2324
        // add any GET params to path
2325
        if (isset($u['query']) && $u['query'] != '') {
2326
            $this->path .= '?' . $u['query'];
2327
        }
2328
2329
        // set default port
2330
        if (!isset($u['port'])) {
2331
            if ($u['scheme'] == 'https') {
2332
                $this->port = 443;
2333
            } else {
2334
                $this->port = 80;
2335
            }
2336
        }
2337
2338
        $this->uri = $this->path;
2339
        $this->digest_uri = $this->uri;
2340
2341
        // build headers
2342
        if (!isset($u['port'])) {
2343
            $this->setHeader('Host', $this->host);
2344
        } else {
2345
            $this->setHeader('Host', $this->host . ':' . $this->port);
2346
        }
2347
2348
        if (isset($u['user']) && $u['user'] != '') {
2349
            $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2350
        }
2351
    }
2352
2353
    /**
2354
     * gets the I/O method to use
2355
     *
2356
     * @return    string    I/O method to use (socket|curl|unknown)
2357
     * @access    private
2358
     */
2359
    function io_method()
2360
    {
2361
        if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm')) {
2362
            return 'curl';
2363
        }
2364
        if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm')) {
2365
            return 'socket';
2366
        }
2367
        return 'unknown';
2368
    }
2369
2370
    /**
2371
     * establish an HTTP connection
2372
     *
2373
     * @param    integer $connection_timeout set connection timeout in seconds
2374
     * @param    integer $response_timeout set response timeout in seconds
2375
     * @return    boolean true if connected, false if not
2376
     * @access   private
2377
     */
2378
    function connect($connection_timeout = 0, $response_timeout = 30)
2379
    {
2380
        // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2381
        // "regular" socket.
2382
        // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2383
        //       loaded), and until PHP5 stream_get_wrappers is not available.
2384
//      if ($this->scheme == 'https') {
2385
//          if (version_compare(phpversion(), '4.3.0') >= 0) {
2386
//              if (extension_loaded('openssl')) {
2387
//                  $this->scheme = 'ssl';
2388
//                  $this->debug('Using SSL over OpenSSL');
2389
//              }
2390
//          }
2391
//      }
2392
        $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2393
        if ($this->io_method() == 'socket') {
2394
            if (!is_array($this->proxy)) {
2395
                $host = $this->host;
2396
            } else {
2397
                $host = $this->proxy['host'];
2398
            }
2399
2400
            // use persistent connection
2401
            if ($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) {
2402
                if (!feof($this->fp)) {
2403
                    $this->debug('Re-use persistent connection');
2404
                    return true;
2405
                }
2406
                fclose($this->fp);
2407
                $this->debug('Closed persistent connection at EOF');
2408
            }
2409
2410
            // munge host if using OpenSSL
2411
            if ($this->scheme == 'ssl') {
2412
                $host = 'ssl://' . $host;
2413
            }
2414
            $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2415
2416
            // open socket
2417
            if ($connection_timeout > 0) {
2418
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2419
            } else {
2420
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str);
2421
            }
2422
2423
            // test pointer
2424
            if (!$this->fp) {
2425
                $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2426
                if ($this->errno) {
2427
                    $msg .= ', Error (' . $this->errno . '): ' . $this->error_str;
2428
                } else {
2429
                    $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2430
                }
2431
                $this->debug($msg);
2432
                $this->setError($msg);
2433
                return false;
2434
            }
2435
2436
            // set response timeout
2437
            $this->debug('set response timeout to ' . $response_timeout);
2438
            socket_set_timeout($this->fp, $response_timeout, 0);
2439
2440
            $this->debug('socket connected');
2441
            return true;
2442
        } elseif ($this->io_method() == 'curl') {
2443
            if (!extension_loaded('curl')) {
2444
//          $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2445
                $this->setError('The PHP cURL Extension is required for HTTPS or NLTM.  You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
2446
                return false;
2447
            }
2448
            // Avoid warnings when PHP does not have these options
2449
            if (defined('CURLOPT_CONNECTIONTIMEOUT')) {
2450
                $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2451
            } else {
2452
                $CURLOPT_CONNECTIONTIMEOUT = 78;
2453
            }
2454
            if (defined('CURLOPT_HTTPAUTH')) {
2455
                $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2456
            } else {
2457
                $CURLOPT_HTTPAUTH = 107;
2458
            }
2459
            if (defined('CURLOPT_PROXYAUTH')) {
2460
                $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2461
            } else {
2462
                $CURLOPT_PROXYAUTH = 111;
2463
            }
2464
            if (defined('CURLAUTH_BASIC')) {
2465
                $CURLAUTH_BASIC = CURLAUTH_BASIC;
2466
            } else {
2467
                $CURLAUTH_BASIC = 1;
2468
            }
2469
            if (defined('CURLAUTH_DIGEST')) {
2470
                $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2471
            } else {
2472
                $CURLAUTH_DIGEST = 2;
2473
            }
2474
            if (defined('CURLAUTH_NTLM')) {
2475
                $CURLAUTH_NTLM = CURLAUTH_NTLM;
2476
            } else {
2477
                $CURLAUTH_NTLM = 8;
2478
            }
2479
2480
            $this->debug('connect using cURL');
2481
            // init CURL
2482
            $this->ch = curl_init();
2483
            // set url
2484
            $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2485
            // add path
2486
            $hostURL .= $this->path;
2487
            $this->setCurlOption(CURLOPT_URL, $hostURL);
2488
            // follow location headers (re-directs)
2489
            if (ini_get('safe_mode') || ini_get('open_basedir')) {
2490
                $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2491
                $this->debug('safe_mode = ');
2492
                $this->appendDebug($this->varDump(ini_get('safe_mode')));
2493
                $this->debug('open_basedir = ');
2494
                $this->appendDebug($this->varDump(ini_get('open_basedir')));
2495
            } else {
2496
                $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2497
            }
2498
            // ask for headers in the response output
2499
            $this->setCurlOption(CURLOPT_HEADER, 1);
2500
            // ask for the response output as the return value
2501
            $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2502
            // encode
2503
            // We manage this ourselves through headers and encoding
2504
//      if(function_exists('gzuncompress')){
2505
//          $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2506
//      }
2507
            // persistent connection
2508
            if ($this->persistentConnection) {
2509
                // I believe the following comment is now bogus, having applied to
2510
                // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2511
                // The way we send data, we cannot use persistent connections, since
2512
                // there will be some "junk" at the end of our request.
2513
                //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2514
                $this->persistentConnection = false;
2515
                $this->setHeader('Connection', 'close');
2516
            }
2517
            // set timeouts
2518
            if ($connection_timeout != 0) {
2519
                $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2520
            }
2521
            if ($response_timeout != 0) {
2522
                $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2523
            }
2524
2525
            if ($this->scheme == 'https') {
2526
                $this->debug('set cURL SSL verify options');
2527
                // recent versions of cURL turn on peer/host checking by default,
2528
                // while PHP binaries are not compiled with a default location for the
2529
                // CA cert bundle, so disable peer/host checking.
2530
                //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2531
                $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2532
                $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2533
2534
                // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2535
                if ($this->authtype == 'certificate') {
2536
                    $this->debug('set cURL certificate options');
2537
                    if (isset($this->certRequest['cainfofile'])) {
2538
                        $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2539
                    }
2540
                    if (isset($this->certRequest['verifypeer'])) {
2541
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2542
                    } else {
2543
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2544
                    }
2545
                    if (isset($this->certRequest['verifyhost'])) {
2546
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2547
                    } else {
2548
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2549
                    }
2550
                    if (isset($this->certRequest['sslcertfile'])) {
2551
                        $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2552
                    }
2553
                    if (isset($this->certRequest['sslkeyfile'])) {
2554
                        $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2555
                    }
2556
                    if (isset($this->certRequest['passphrase'])) {
2557
                        $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2558
                    }
2559
                    if (isset($this->certRequest['certpassword'])) {
2560
                        $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2561
                    }
2562
                }
2563
            }
2564
            if ($this->authtype && ($this->authtype != 'certificate')) {
2565
                if ($this->username) {
2566
                    $this->debug('set cURL username/password');
2567
                    $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2568
                }
2569
                if ($this->authtype == 'basic') {
2570
                    $this->debug('set cURL for Basic authentication');
2571
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2572
                }
2573
                if ($this->authtype == 'digest') {
2574
                    $this->debug('set cURL for digest authentication');
2575
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2576
                }
2577
                if ($this->authtype == 'ntlm') {
2578
                    $this->debug('set cURL for NTLM authentication');
2579
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2580
                }
2581
            }
2582
            if (is_array($this->proxy)) {
2583
                $this->debug('set cURL proxy options');
2584
                if ($this->proxy['port'] != '') {
2585
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'] . ':' . $this->proxy['port']);
2586
                } else {
2587
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2588
                }
2589
                if ($this->proxy['username'] || $this->proxy['password']) {
2590
                    $this->debug('set cURL proxy authentication options');
2591
                    $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'] . ':' . $this->proxy['password']);
2592
                    if ($this->proxy['authtype'] == 'basic') {
2593
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2594
                    }
2595
                    if ($this->proxy['authtype'] == 'ntlm') {
2596
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2597
                    }
2598
                }
2599
            }
2600
            $this->debug('cURL connection set up');
2601
            return true;
2602
        } else {
2603
            $this->setError('Unknown scheme ' . $this->scheme);
2604
            $this->debug('Unknown scheme ' . $this->scheme);
2605
            return false;
2606
        }
2607
    }
2608
2609
    /**
2610
     * sends the SOAP request and gets the SOAP response via HTTP[S]
2611
     *
2612
     * @param    string $data message data
2613
     * @param    integer $timeout set connection timeout in seconds
2614
     * @param    integer $response_timeout set response timeout in seconds
2615
     * @param    array $cookies cookies to send
2616
     * @return    string data
2617
     * @access   public
2618
     */
2619
    function send($data, $timeout = 0, $response_timeout = 30, $cookies = null)
2620
    {
2621
        $this->debug('entered send() with data of length: ' . strlen($data));
2622
2623
        $respdata = "";
2624
        $this->tryagain = true;
2625
        $tries = 0;
2626
        while ($this->tryagain) {
2627
            $this->tryagain = false;
2628
            if ($tries++ < 2) {
2629
                // make connnection
2630
                if (!$this->connect($timeout, $response_timeout)) {
2631
                    return false;
2632
                }
2633
2634
                // send request
2635
                if (!$this->sendRequest($data, $cookies)) {
2636
                    return false;
2637
                }
2638
2639
                // get response
2640
                $respdata = $this->getResponse();
2641
            } else {
2642
                $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2643
            }
2644
        }
2645
        $this->debug('end of send()');
2646
        return $respdata;
2647
    }
2648
2649
2650
    /**
2651
     * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2652
     *
2653
     * @param    string $data message data
2654
     * @param    integer $timeout set connection timeout in seconds
2655
     * @param    integer $response_timeout set response timeout in seconds
2656
     * @param    array $cookies cookies to send
2657
     * @return    string data
2658
     * @access   public
2659
     */
2660
    function sendHTTPS($data, $timeout = 0, $response_timeout = 30, $cookies = NULL)
2661
    {
2662
        return $this->send($data, $timeout, $response_timeout, $cookies);
2663
    }
2664
2665
    /**
2666
     * if authenticating, set user credentials here
2667
     *
2668
     * @param    string $username
2669
     * @param    string $password
2670
     * @param    string $authtype (basic|digest|certificate|ntlm)
2671
     * @param    array $digestRequest (keys must be nonce, nc, realm, qop)
2672
     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2673
     * @access   public
2674
     */
2675
    function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array())
2676
    {
2677
        $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2678
        $this->appendDebug($this->varDump($digestRequest));
2679
        $this->debug("certRequest=");
2680
        $this->appendDebug($this->varDump($certRequest));
2681
        // cf. RFC 2617
2682
        if ($authtype == 'basic') {
2683
            $this->setHeader('Authorization', 'Basic ' . base64_encode(str_replace(':', '', $username) . ':' . $password));
2684
        } elseif ($authtype == 'digest') {
2685
            if (isset($digestRequest['nonce'])) {
2686
                $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2687
2688
                // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2689
2690
                // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2691
                $A1 = $username . ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2692
2693
                // H(A1) = MD5(A1)
2694
                $HA1 = md5($A1);
2695
2696
                // A2 = Method ":" digest-uri-value
2697
                $A2 = $this->request_method . ':' . $this->digest_uri;
2698
2699
                // H(A2)
2700
                $HA2 = md5($A2);
2701
2702
                // KD(secret, data) = H(concat(secret, ":", data))
2703
                // if qop == auth:
2704
                // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2705
                //                              ":" nc-value
2706
                //                              ":" unq(cnonce-value)
2707
                //                              ":" unq(qop-value)
2708
                //                              ":" H(A2)
2709
                //                            ) <">
2710
                // if qop is missing,
2711
                // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2712
2713
                $nonce = $digestRequest['nonce'];
2714
                $cnonce = $nonce;
2715
                if ($digestRequest['qop'] != '') {
2716
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2717
                } else {
2718
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2719
                }
2720
2721
                $hashedDigest = md5($unhashedDigest);
2722
2723
                $opaque = '';
2724
                if (isset($digestRequest['opaque'])) {
2725
                    $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2726
                }
2727
2728
                $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
2729
            }
2730
        } elseif ($authtype == 'certificate') {
2731
            $this->certRequest = $certRequest;
2732
            $this->debug('Authorization header not set for certificate');
2733
        } elseif ($authtype == 'ntlm') {
2734
            // do nothing
2735
            $this->debug('Authorization header not set for ntlm');
2736
        }
2737
        $this->username = $username;
2738
        $this->password = $password;
2739
        $this->authtype = $authtype;
2740
        $this->digestRequest = $digestRequest;
2741
    }
2742
2743
    /**
2744
     * set the soapaction value
2745
     *
2746
     * @param    string $soapaction
2747
     * @access   public
2748
     */
2749
    function setSOAPAction($soapaction)
2750
    {
2751
        $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2752
    }
2753
2754
    /**
2755
     * use http encoding
2756
     *
2757
     * @param    string $enc encoding style. supported values: gzip, deflate, or both
2758
     * @access   public
2759
     */
2760
    function setEncoding($enc = 'gzip, deflate')
2761
    {
2762
        if (function_exists('gzdeflate')) {
2763
            $this->protocol_version = '1.1';
2764
            $this->setHeader('Accept-Encoding', $enc);
2765
            if (!isset($this->outgoing_headers['Connection'])) {
2766
                $this->setHeader('Connection', 'close');
2767
                $this->persistentConnection = false;
2768
            }
2769
            // deprecated as of PHP 5.3.0
2770
            //set_magic_quotes_runtime(0);
2771
            $this->encoding = $enc;
2772
        }
2773
    }
2774
2775
    /**
2776
     * set proxy info here
2777
     *
2778
     * @param    string $proxyhost use an empty string to remove proxy
2779
     * @param    string $proxyport
2780
     * @param    string $proxyusername
2781
     * @param    string $proxypassword
2782
     * @param    string $proxyauthtype (basic|ntlm)
2783
     * @access   public
2784
     */
2785
    function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic')
2786
    {
2787
        if ($proxyhost) {
2788
            $this->proxy = array(
2789
                'host' => $proxyhost,
2790
                'port' => $proxyport,
2791
                'username' => $proxyusername,
2792
                'password' => $proxypassword,
2793
                'authtype' => $proxyauthtype
2794
            );
2795
            if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype == 'basic') {
2796
                $this->setHeader('Proxy-Authorization', ' Basic ' . base64_encode($proxyusername . ':' . $proxypassword));
2797
            }
2798
        } else {
2799
            $this->debug('remove proxy');
2800
            $this->unsetHeader('Proxy-Authorization');
2801
        }
2802
    }
2803
2804
2805
    /**
2806
     * Test if the given string starts with a header that is to be skipped.
2807
     * Skippable headers result from chunked transfer and proxy requests.
2808
     *
2809
     * @param    string $data The string to check.
2810
     * @returns    boolean    Whether a skippable header was found.
2811
     * @access    private
2812
     */
2813
    function isSkippableCurlHeader($data)
2814
    {
2815
        $skipHeaders = array('HTTP/1.1 100',
2816
            'HTTP/1.0 301',
2817
            'HTTP/1.1 301',
2818
            'HTTP/1.0 302',
2819
            'HTTP/1.1 302',
2820
            'HTTP/1.0 401',
2821
            'HTTP/1.1 401',
2822
            'HTTP/1.0 200 Connection established',
2823
            'HTTP/1.1 200 Connection established');
2824
        foreach ($skipHeaders as $hd) {
2825
            $prefix = substr($data, 0, strlen($hd));
2826
            if ($prefix == $hd) {
2827
                return true;
2828
            }
2829
        }
2830
2831
        return false;
2832
    }
2833
2834
    /**
2835
     * decode a string that is encoded w/ "chunked' transfer encoding
2836
     * as defined in RFC2068 19.4.6
2837
     *
2838
     * @param    string $buffer
2839
     * @param    string $lb
2840
     * @returns    string
2841
     * @access   public
2842
     * @deprecated
2843
     */
2844
    function decodeChunked($buffer, $lb)
2845
    {
2846
        $new = '';
2847
2848
        // read chunk-size, chunk-extension (if any) and CRLF
2849
        // get the position of the linebreak
2850
        $chunkend = strpos($buffer, $lb);
2851
        if (!$chunkend) {
2852
            $this->debug('no linebreak found in decodeChunked');
2853
            return $new;
2854
        }
2855
        $temp = substr($buffer, 0, $chunkend);
2856
        $chunk_size = hexdec(trim($temp));
2857
        $chunkstart = $chunkend + strlen($lb);
2858
        // while (chunk-size > 0) {
2859
        while ($chunk_size > 0) {
2860
            $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2861
            $chunkend = strpos($buffer, $lb, $chunkstart + $chunk_size);
2862
2863
            // Just in case we got a broken connection
2864
            if (!$chunkend) {
2865
                $chunk = substr($buffer, $chunkstart);
2866
                // append chunk-data to entity-body
2867
                $new .= $chunk;
2868
                break;
2869
            }
2870
2871
            // read chunk-data and CRLF
2872
            $chunk = substr($buffer, $chunkstart, $chunkend - $chunkstart);
2873
            // append chunk-data to entity-body
2874
            $new .= $chunk;
2875
            // length := length + chunk-size
2876
            // read chunk-size and CRLF
2877
            $chunkstart = $chunkend + strlen($lb);
2878
2879
            $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2880
            if (!$chunkend) {
2881
                break; //Just in case we got a broken connection
2882
            }
2883
            $temp = substr($buffer, $chunkstart, $chunkend - $chunkstart);
2884
            $chunk_size = hexdec(trim($temp));
2885
            $chunkstart = $chunkend;
2886
        }
2887
        return $new;
2888
    }
2889
2890
    /**
2891
     * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2892
     *
2893
     * @param    string $data HTTP body
2894
     * @param    string $cookie_str data for HTTP Cookie header
2895
     * @return    void
2896
     * @access    private
2897
     */
2898
    function buildPayload($data, $cookie_str = '')
2899
    {
2900
        // Note: for cURL connections, $this->outgoing_payload is ignored,
2901
        // as is the Content-Length header, but these are still created as
2902
        // debugging guides.
2903
2904
        // add content-length header
2905
        if ($this->request_method != 'GET') {
2906
            $this->setHeader('Content-Length', strlen($data));
2907
        }
2908
2909
        // start building outgoing payload:
2910
        if ($this->proxy) {
2911
            $uri = $this->url;
2912
        } else {
2913
            $uri = $this->uri;
2914
        }
2915
        $req = "$this->request_method $uri HTTP/$this->protocol_version";
2916
        $this->debug("HTTP request: $req");
2917
        $this->outgoing_payload = "$req\r\n";
2918
2919
        // loop thru headers, serializing
2920
        foreach ($this->outgoing_headers as $k => $v) {
2921
            $hdr = $k . ': ' . $v;
2922
            $this->debug("HTTP header: $hdr");
2923
            $this->outgoing_payload .= "$hdr\r\n";
2924
        }
2925
2926
        // add any cookies
2927
        if ($cookie_str != '') {
2928
            $hdr = 'Cookie: ' . $cookie_str;
2929
            $this->debug("HTTP header: $hdr");
2930
            $this->outgoing_payload .= "$hdr\r\n";
2931
        }
2932
2933
        // header/body separator
2934
        $this->outgoing_payload .= "\r\n";
2935
2936
        // add data
2937
        $this->outgoing_payload .= $data;
2938
    }
2939
2940
    /**
2941
     * sends the SOAP request via HTTP[S]
2942
     *
2943
     * @param    string $data message data
2944
     * @param    array $cookies cookies to send
2945
     * @return    boolean    true if OK, false if problem
2946
     * @access   private
2947
     */
2948
    function sendRequest($data, $cookies = null)
2949
    {
2950
        // build cookie string
2951
        $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2952
2953
        // build payload
2954
        $this->buildPayload($data, $cookie_str);
2955
2956
        if ($this->io_method() == 'socket') {
2957
            // send payload
2958
            if (!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2959
                $this->setError('couldn\'t write message data to socket');
2960
                $this->debug('couldn\'t write message data to socket');
2961
                return false;
2962
            }
2963
            $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2964
            return true;
2965
        } elseif ($this->io_method() == 'curl') {
2966
            // set payload
2967
            // cURL does say this should only be the verb, and in fact it
2968
            // turns out that the URI and HTTP version are appended to this, which
2969
            // some servers refuse to work with (so we no longer use this method!)
2970
            //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2971
            $curl_headers = array();
2972
            foreach ($this->outgoing_headers as $k => $v) {
2973
                if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2974
                    $this->debug("Skip cURL header $k: $v");
2975
                } else {
2976
                    $curl_headers[] = "$k: $v";
2977
                }
2978
            }
2979
            if ($cookie_str != '') {
2980
                $curl_headers[] = 'Cookie: ' . $cookie_str;
2981
            }
2982
            $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2983
            $this->debug('set cURL HTTP headers');
2984
            if ($this->request_method == "POST") {
2985
                $this->setCurlOption(CURLOPT_POST, 1);
2986
                $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2987
                $this->debug('set cURL POST data');
2988
            }
2989
            // insert custom user-set cURL options
2990
            foreach ($this->ch_options as $key => $val) {
2991
                $this->setCurlOption($key, $val);
2992
            }
2993
2994
            $this->debug('set cURL payload');
2995
            return true;
2996
        }
2997
        return false;
2998
    }
2999
3000
    /**
3001
     * gets the SOAP response via HTTP[S]
3002
     *
3003
     * @return    string the response (also sets member variables like incoming_payload)
3004
     * @access   private
3005
     */
3006
    function getResponse()
3007
    {
3008
        $this->incoming_payload = '';
3009
        $header_array = array ();
3010
        $data = '';
3011
3012
        if ($this->io_method() == 'socket') {
3013
            // loop until headers have been retrieved
3014
            $pos = 0;
3015
            while (!isset($lb)) {
3016
                // We might EOF during header read.
3017
                if (feof($this->fp)) {
3018
                    $this->incoming_payload = $data;
3019
                    $this->debug('found no headers before EOF after length ' . strlen($data));
3020
                    $this->debug("received before EOF:\n" . $data);
3021
                    $this->setError('server failed to send headers');
3022
                    return false;
3023
                }
3024
3025
                $tmp = fgets($this->fp, 256);
3026
                $tmplen = strlen($tmp);
3027
                $this->debug("read line of $tmplen bytes: " . trim($tmp));
3028
3029
                if ($tmplen == 0) {
3030
                    $this->incoming_payload = $data;
3031
                    $this->debug('socket read of headers timed out after length ' . strlen($data));
3032
                    $this->debug("read before timeout: " . $data);
3033
                    $this->setError('socket read of headers timed out');
3034
                    return false;
3035
                }
3036
3037
                $data .= $tmp;
3038
                $pos = strpos($data, "\r\n\r\n");
3039
                if ($pos > 1) {
3040
                    $lb = "\r\n";
3041
                } else {
3042
                    $pos = strpos($data, "\n\n");
3043
                    if ($pos > 1) {
3044
                        $lb = "\n";
3045
                    }
3046
                }
3047
                // remove 100 headers
3048
                if (isset($lb) && preg_match('/^HTTP\/1.1 100/', $data)) {
3049
                    unset($lb);
3050
                    $data = '';
3051
                }//
3052
            }
3053
            // store header data
3054
            $this->incoming_payload .= $data;
3055
            $this->debug('found end of headers after length ' . strlen($data));
3056
            // process headers
3057
            $header_data = trim(substr($data, 0, $pos));
3058
            $header_array = explode($lb, $header_data);
3059
            $this->incoming_headers = array();
3060
            $this->incoming_cookies = array();
3061
            foreach ($header_array as $header_line) {
3062
                $arr = explode(':', $header_line, 2);
3063
                if (count($arr) > 1) {
3064
                    $header_name = strtolower(trim($arr[0]));
3065
                    $this->incoming_headers[$header_name] = trim($arr[1]);
3066
                    if ($header_name == 'set-cookie') {
3067
                        // TODO: allow multiple cookies from parseCookie
3068
                        $cookie = $this->parseCookie(trim($arr[1]));
3069
                        if ($cookie) {
3070
                            $this->incoming_cookies[] = $cookie;
3071
                            $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3072
                        } else {
3073
                            $this->debug('did not find cookie in ' . trim($arr[1]));
3074
                        }
3075
                    }
3076
                } elseif (isset($header_name)) {
3077
                    // append continuation line to previous header
3078
                    $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3079
                }
3080
            }
3081
3082
            // loop until msg has been received
3083
            if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
3084
                $content_length = 2147483647;    // ignore any content-length header
3085
                $chunked = true;
3086
                $this->debug("want to read chunked content");
3087
            } elseif (isset($this->incoming_headers['content-length'])) {
3088
                $content_length = $this->incoming_headers['content-length'];
3089
                $chunked = false;
3090
                $this->debug("want to read content of length $content_length");
3091
            } else {
3092
                $content_length = 2147483647;
3093
                $chunked = false;
3094
                $this->debug("want to read content to EOF");
3095
            }
3096
            $data = '';
3097
            do {
3098
                if ($chunked) {
3099
                    $tmp = fgets($this->fp, 256);
3100
                    $tmplen = strlen($tmp);
3101
                    $this->debug("read chunk line of $tmplen bytes");
3102
                    if ($tmplen == 0) {
3103
                        $this->incoming_payload = $data;
3104
                        $this->debug('socket read of chunk length timed out after length ' . strlen($data));
3105
                        $this->debug("read before timeout:\n" . $data);
3106
                        $this->setError('socket read of chunk length timed out');
3107
                        return false;
3108
                    }
3109
                    $content_length = hexdec(trim($tmp));
3110
                    $this->debug("chunk length $content_length");
3111
                }
3112
                $strlen = 0;
3113
                while (($strlen < $content_length) && (!feof($this->fp))) {
3114
                    $readlen = min(8192, $content_length - $strlen);
3115
                    $tmp = fread($this->fp, $readlen);
3116
                    $tmplen = strlen($tmp);
3117
                    $this->debug("read buffer of $tmplen bytes");
3118
                    if (($tmplen == 0) && (!feof($this->fp))) {
3119
                        $this->incoming_payload = $data;
3120
                        $this->debug('socket read of body timed out after length ' . strlen($data));
3121
                        $this->debug("read before timeout:\n" . $data);
3122
                        $this->setError('socket read of body timed out');
3123
                        return false;
3124
                    }
3125
                    $strlen += $tmplen;
3126
                    $data .= $tmp;
3127
                }
3128
                if ($chunked && ($content_length > 0)) {
3129
                    $tmp = fgets($this->fp, 256);
3130
                    $tmplen = strlen($tmp);
3131
                    $this->debug("read chunk terminator of $tmplen bytes");
3132
                    if ($tmplen == 0) {
3133
                        $this->incoming_payload = $data;
3134
                        $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3135
                        $this->debug("read before timeout:\n" . $data);
3136
                        $this->setError('socket read of chunk terminator timed out');
3137
                        return false;
3138
                    }
3139
                }
3140
            } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3141
            if (feof($this->fp)) {
3142
                $this->debug('read to EOF');
3143
            }
3144
            $this->debug('read body of length ' . strlen($data));
3145
            $this->incoming_payload .= $data;
3146
            $this->debug('received a total of ' . strlen($this->incoming_payload) . ' bytes of data from server');
3147
3148
            // close filepointer
3149
            if (
3150
                (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
3151
                (!$this->persistentConnection) || feof($this->fp)
3152
            ) {
3153
                fclose($this->fp);
3154
                $this->fp = false;
3155
                $this->debug('closed socket');
3156
            }
3157
3158
            // connection was closed unexpectedly
3159
            if ($this->incoming_payload == '') {
3160
                $this->setError('no response from server');
3161
                return false;
3162
            }
3163
3164
            // decode transfer-encoding
3165
//      if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3166
//          if(!$data = $this->decodeChunked($data, $lb)){
3167
//              $this->setError('Decoding of chunked data failed');
3168
//              return false;
3169
//          }
3170
            //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3171
            // set decoded payload
3172
//          $this->incoming_payload = $header_data.$lb.$lb.$data;
3173
//      }
3174
        } elseif ($this->io_method() == 'curl') {
3175
            // send and receive
3176
            $this->debug('send and receive with cURL');
3177
            $this->incoming_payload = curl_exec($this->ch);
3178
            $data = $this->incoming_payload;
3179
3180
            $cErr = curl_error($this->ch);
3181
            if ($cErr != '') {
3182
                $err = 'cURL ERROR: ' . curl_errno($this->ch) . ': ' . $cErr . '<br>';
3183
                // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3184
                foreach (curl_getinfo($this->ch) as $k => $v) {
3185
                    if (is_array($v)) {
3186
                        $this->debug("$k: " . json_encode($v));
3187
                    } else {
3188
                        $this->debug("$k: $v<br>");
3189
                    }
3190
                }
3191
                $this->debug($err);
3192
                $this->setError($err);
3193
                curl_close($this->ch);
3194
                return false;
3195
            }
3196
            // close curl
3197
            $this->debug('No cURL error, closing cURL');
3198
            curl_close($this->ch);
3199
3200
            // try removing skippable headers
3201
            $savedata = $data;
3202
            while ($this->isSkippableCurlHeader($data)) {
3203
                $this->debug("Found HTTP header to skip");
3204
                if ($pos = strpos($data, "\r\n\r\n")) {
3205
                    $data = ltrim(substr($data, $pos));
3206
                } elseif ($pos = strpos($data, "\n\n")) {
3207
                    $data = ltrim(substr($data, $pos));
3208
                }
3209
            }
3210
3211
            if ($data == '') {
3212
                // have nothing left; just remove 100 header(s)
3213
                $data = $savedata;
3214
                while (preg_match('/^HTTP\/1.1 100/', $data)) {
3215
                    if ($pos = strpos($data, "\r\n\r\n")) {
3216
                        $data = ltrim(substr($data, $pos));
3217
                    } elseif ($pos = strpos($data, "\n\n")) {
3218
                        $data = ltrim(substr($data, $pos));
3219
                    }
3220
                }
3221
            }
3222
3223
            // separate content from HTTP headers
3224
            if ($pos = strpos($data, "\r\n\r\n")) {
3225
                $lb = "\r\n";
3226
            } elseif ($pos = strpos($data, "\n\n")) {
3227
                $lb = "\n";
3228
            } else {
3229
                $this->debug('no proper separation of headers and document');
3230
                $this->setError('no proper separation of headers and document');
3231
                return false;
3232
            }
3233
            $header_data = trim(substr($data, 0, $pos));
3234
            $header_array = explode($lb, $header_data);
3235
            $data = ltrim(substr($data, $pos));
3236
            $this->debug('found proper separation of headers and document');
3237
            $this->debug('cleaned data, stringlen: ' . strlen($data));
3238
            // clean headers
3239
            foreach ($header_array as $header_line) {
3240
                $arr = explode(':', $header_line, 2);
3241
                if (count($arr) > 1) {
3242
                    $header_name = strtolower(trim($arr[0]));
3243
                    $this->incoming_headers[$header_name] = trim($arr[1]);
3244
                    if ($header_name == 'set-cookie') {
3245
                        // TODO: allow multiple cookies from parseCookie
3246
                        $cookie = $this->parseCookie(trim($arr[1]));
3247
                        if ($cookie) {
3248
                            $this->incoming_cookies[] = $cookie;
3249
                            $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3250
                        } else {
3251
                            $this->debug('did not find cookie in ' . trim($arr[1]));
3252
                        }
3253
                    }
3254
                } elseif (isset($header_name)) {
3255
                    // append continuation line to previous header
3256
                    $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3257
                }
3258
            }
3259
        }
3260
3261
        $this->response_status_line = $header_array[0];
3262
        $arr = explode(' ', $this->response_status_line, 3);
3263
        $http_status = intval($arr[1]);
3264
        $http_reason = count($arr) > 2 ? $arr[2] : '';
3265
3266
        // see if we need to resend the request with http digest authentication
3267
        if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3268
            $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3269
            $this->setURL($this->incoming_headers['location']);
3270
            $this->tryagain = true;
3271
            return false;
3272
        }
3273
3274
        // see if we need to resend the request with http digest authentication
3275
        if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3276
            $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3277
            if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3278
                $this->debug('Server wants digest authentication');
3279
                // remove "Digest " from our elements
3280
                $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3281
3282
                // parse elements into array
3283
                $digestElements = explode(',', $digestString);
3284
                foreach ($digestElements as $val) {
3285
                    $tempElement = explode('=', trim($val), 2);
3286
                    $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3287
                }
3288
3289
                // should have (at least) qop, realm, nonce
3290
                if (isset($digestRequest['nonce'])) {
3291
                    $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3292
                    $this->tryagain = true;
3293
                    return false;
3294
                }
3295
            }
3296
            $this->debug('HTTP authentication failed');
3297
            $this->setError('HTTP authentication failed');
3298
            return false;
3299
        }
3300
3301
        if (
3302
            ($http_status >= 300 && $http_status <= 307) ||
3303
            ($http_status >= 400 && $http_status <= 417) ||
3304
            ($http_status >= 501 && $http_status <= 505)
3305
        ) {
3306
            $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3307
            return false;
3308
        }
3309
3310
        // decode content-encoding
3311
        if (isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != '') {
3312
            if (strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip') {
3313
                $header_data = "";
3314
                // if decoding works, use it. else assume data wasn't gzencoded
3315
                if (function_exists('gzinflate')) {
3316
                    //$timer->setMarker('starting decoding of gzip/deflated content');
3317
                    // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3318
                    // this means there are no Zlib headers, although there should be
3319
                    $this->debug('The gzinflate function exists');
3320
                    $datalen = strlen($data);
3321
                    if ($this->incoming_headers['content-encoding'] == 'deflate') {
3322
                        if ($degzdata = @gzinflate($data)) {
3323
                            $data = $degzdata;
3324
                            $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3325
                            if (strlen($data) < $datalen) {
3326
                                // test for the case that the payload has been compressed twice
3327
                                $this->debug('The inflated payload is smaller than the gzipped one; try again');
3328
                                if ($degzdata = @gzinflate($data)) {
3329
                                    $data = $degzdata;
3330
                                    $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3331
                                }
3332
                            }
3333
                        } else {
3334
                            $this->debug('Error using gzinflate to inflate the payload');
3335
                            $this->setError('Error using gzinflate to inflate the payload');
3336
                        }
3337
                    } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3338
                        if ($degzdata = @gzinflate(substr($data, 10))) {    // do our best
3339
                            $data = $degzdata;
3340
                            $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3341
                            if (strlen($data) < $datalen) {
3342
                                // test for the case that the payload has been compressed twice
3343
                                $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3344
                                if ($degzdata = @gzinflate(substr($data, 10))) {
3345
                                    $data = $degzdata;
3346
                                    $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3347
                                }
3348
                            }
3349
                        } else {
3350
                            $this->debug('Error using gzinflate to un-gzip the payload');
3351
                            $this->setError('Error using gzinflate to un-gzip the payload');
3352
                        }
3353
                    }
3354
                    //$timer->setMarker('finished decoding of gzip/deflated content');
3355
                    //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3356
                    // set decoded payload
3357
                    $this->incoming_payload = $header_data . (isset($lb) ? $lb : "") . (isset($lb) ? $lb : "") . $data;
3358
                } else {
3359
                    $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3360
                    $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3361
                }
3362
            } else {
3363
                $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3364
                $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3365
            }
3366
        } else {
3367
            $this->debug('No Content-Encoding header');
3368
        }
3369
3370
        if (strlen($data) == 0) {
3371
            $this->debug('no data after headers!');
3372
            $this->setError('no data present after HTTP headers');
3373
            return false;
3374
        }
3375
3376
        return $data;
3377
    }
3378
3379
    /**
3380
     * sets the content-type for the SOAP message to be sent
3381
     *
3382
     * @param    string $type the content type, MIME style
3383
     * @param    mixed $charset character set used for encoding (or false)
3384
     * @access    public
3385
     */
3386
    function setContentType($type, $charset = false)
3387
    {
3388
        $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3389
    }
3390
3391
    /**
3392
     * specifies that an HTTP persistent connection should be used
3393
     *
3394
     * @return    boolean whether the request was honored by this method.
3395
     * @access    public
3396
     */
3397
    function usePersistentConnection()
3398
    {
3399
        if (isset($this->outgoing_headers['Accept-Encoding'])) {
3400
            return false;
3401
        }
3402
        $this->protocol_version = '1.1';
3403
        $this->persistentConnection = true;
3404
        $this->setHeader('Connection', 'Keep-Alive');
3405
        return true;
3406
    }
3407
3408
    /**
3409
     * parse an incoming Cookie into it's parts
3410
     *
3411
     * @param    string $cookie_str content of cookie
3412
     * @return    array with data of that cookie
3413
     * @access    private
3414
     */
3415
    /*
3416
     * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3417
     */
3418
    function parseCookie($cookie_str)
3419
    {
3420
        $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3421
        $data = explode(';', $cookie_str);
3422
        $value_str = $data[0];
3423
3424
        $cookie_param = 'domain=';
3425
        $start = strpos($cookie_str, $cookie_param);
3426
        if ($start > 0) {
3427
            $domain = substr($cookie_str, $start + strlen($cookie_param));
3428
            $domain = substr($domain, 0, strpos($domain, ';'));
3429
        } else {
3430
            $domain = '';
3431
        }
3432
3433
        $cookie_param = 'expires=';
3434
        $start = strpos($cookie_str, $cookie_param);
3435
        if ($start > 0) {
3436
            $expires = substr($cookie_str, $start + strlen($cookie_param));
3437
            $expires = substr($expires, 0, strpos($expires, ';'));
3438
        } else {
3439
            $expires = '';
3440
        }
3441
3442
        $cookie_param = 'path=';
3443
        $start = strpos($cookie_str, $cookie_param);
3444
        if ($start > 0) {
3445
            $path = substr($cookie_str, $start + strlen($cookie_param));
3446
            $path = substr($path, 0, strpos($path, ';'));
3447
        } else {
3448
            $path = '/';
3449
        }
3450
3451
        $cookie_param = ';secure;';
3452
        if (strpos($cookie_str, $cookie_param) !== false) {
3453
            $secure = true;
3454
        } else {
3455
            $secure = false;
3456
        }
3457
3458
        $sep_pos = strpos($value_str, '=');
3459
3460
        if ($sep_pos) {
3461
            $name = substr($value_str, 0, $sep_pos);
3462
            $value = substr($value_str, $sep_pos + 1);
3463
3464
          return array('name' => $name,
3465
                     'value' => $value,
3466
                     'domain' => $domain,
3467
                     'path' => $path,
3468
                     'expires' => $expires,
3469
                     'secure' => $secure
3470
          );
3471
        }
3472
        return array ();
3473
    }
3474
3475
    /**
3476
     * sort out cookies for the current request
3477
     *
3478
     * @param    array $cookies array with all cookies
3479
     * @param    boolean $secure is the send-content secure or not?
3480
     * @return    string for Cookie-HTTP-Header
3481
     * @access    private
3482
     */
3483
    function getCookiesForRequest($cookies, $secure = false)
3484
    {
3485
        $cookie_str = '';
3486
        if ((is_array($cookies))) {
3487
            foreach ($cookies as $cookie) {
3488
                if (!is_array($cookie)) {
3489
                    continue;
3490
                }
3491
                $this->debug("check cookie for validity: " . $cookie['name'] . '=' . $cookie['value']);
3492
                if ((isset($cookie['expires'])) && (!empty($cookie['expires']))) {
3493
                    if (strtotime($cookie['expires']) <= time()) {
3494
                        $this->debug('cookie has expired');
3495
                        continue;
3496
                    }
3497
                }
3498
                if ((isset($cookie['domain'])) && (!empty($cookie['domain']))) {
3499
                    $domain = preg_quote($cookie['domain'], "'");
3500
                    if (!preg_match("'.*$domain$'i", $this->host)) {
3501
                        $this->debug('cookie has different domain');
3502
                        continue;
3503
                    }
3504
                }
3505
                if ((isset($cookie['path'])) && (!empty($cookie['path']))) {
3506
                    $path = preg_quote($cookie['path'], "'");
3507
                    if (!preg_match("'^$path.*'i", $this->path)) {
3508
                        $this->debug('cookie is for a different path');
3509
                        continue;
3510
                    }
3511
                }
3512
                if ((!$secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3513
                    $this->debug('cookie is secure, transport is not');
3514
                    continue;
3515
                }
3516
                $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3517
                $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3518
            }
3519
        }
3520
        return $cookie_str;
3521
    }
3522
}
3523
3524
3525
/**
3526
 *
3527
 * nusoap_server allows the user to create a SOAP server
3528
 * that is capable of receiving messages and returning responses
3529
 *
3530
 * @author   Dietrich Ayala <[email protected]>
3531
 * @author   Scott Nichol <[email protected]>
3532
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
3533
 * @access   public
3534
 */
3535
class nusoap_server extends nusoap_base
3536
{
3537
    /**
3538
     * HTTP headers of request
3539
     *
3540
     * @var array
3541
     * @access private
3542
     */
3543
    var $headers = array();
3544
    /**
3545
     * HTTP request
3546
     *
3547
     * @var string
3548
     * @access private
3549
     */
3550
    var $request = '';
3551
    /**
3552
     * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3553
     *
3554
     * @var string
3555
     * @access public
3556
     */
3557
    var $requestHeaders = '';
3558
    /**
3559
     * SOAP Headers from request (parsed)
3560
     *
3561
     * @var mixed
3562
     * @access public
3563
     */
3564
    var $requestHeader = null;
3565
    /**
3566
     * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3567
     *
3568
     * @var string
3569
     * @access public
3570
     */
3571
    var $document = '';
3572
    /**
3573
     * SOAP payload for request (text)
3574
     *
3575
     * @var string
3576
     * @access public
3577
     */
3578
    var $requestSOAP = '';
3579
    /**
3580
     * requested method namespace URI
3581
     *
3582
     * @var string
3583
     * @access private
3584
     */
3585
    var $methodURI = '';
3586
    /**
3587
     * name of method requested
3588
     *
3589
     * @var string
3590
     * @access private
3591
     */
3592
    var $methodname = '';
3593
    /**
3594
     * name of the response tag name
3595
     *
3596
     * @var string
3597
     * @access private
3598
     */
3599
    var $responseTagName = '';
3600
    /**
3601
     * method parameters from request
3602
     *
3603
     * @var array
3604
     * @access private
3605
     */
3606
    var $methodparams = array();
3607
    /**
3608
     * SOAP Action from request
3609
     *
3610
     * @var string
3611
     * @access private
3612
     */
3613
    var $SOAPAction = '';
3614
    /**
3615
     * character set encoding of incoming (request) messages
3616
     *
3617
     * @var string
3618
     * @access public
3619
     */
3620
    var $xml_encoding = '';
3621
    /**
3622
     * toggles whether the parser decodes element content w/ utf8_decode()
3623
     *
3624
     * @var boolean
3625
     * @access public
3626
     */
3627
    var $decode_utf8 = true;
3628
3629
    /**
3630
     * HTTP headers of response
3631
     *
3632
     * @var array
3633
     * @access public
3634
     */
3635
    var $outgoing_headers = array();
3636
    /**
3637
     * HTTP response
3638
     *
3639
     * @var string
3640
     * @access private
3641
     */
3642
    var $response = '';
3643
    /**
3644
     * SOAP headers for response (text or array of soapval or associative array)
3645
     *
3646
     * @var mixed
3647
     * @access public
3648
     */
3649
    var $responseHeaders = '';
3650
    /**
3651
     * SOAP payload for response (text)
3652
     *
3653
     * @var string
3654
     * @access private
3655
     */
3656
    var $responseSOAP = '';
3657
    /**
3658
     * SOAP attachments in response
3659
     *
3660
     * @var string
3661
     * @access private
3662
     */
3663
    var $attachments = '';
3664
    /**
3665
     * method return value to place in response
3666
     *
3667
     * @var mixed
3668
     * @access private
3669
     */
3670
    var $methodreturn = false;
3671
    /**
3672
     * whether $methodreturn is a string of literal XML
3673
     *
3674
     * @var boolean
3675
     * @access public
3676
     */
3677
    var $methodreturnisliteralxml = false;
3678
    /**
3679
     * SOAP fault for response (or false)
3680
     *
3681
     * @var mixed
3682
     * @access private
3683
     */
3684
    var $fault = false;
3685
    /**
3686
     * text indication of result (for debugging)
3687
     *
3688
     * @var string
3689
     * @access private
3690
     */
3691
    var $result = 'successful';
3692
3693
    /**
3694
     * assoc array of operations => opData; operations are added by the register()
3695
     * method or by parsing an external WSDL definition
3696
     *
3697
     * @var array
3698
     * @access private
3699
     */
3700
    var $operations = array();
3701
    /**
3702
     * wsdl instance (if one)
3703
     *
3704
     * @var mixed
3705
     * @access private
3706
     */
3707
    var $wsdl = false;
3708
    /**
3709
     * URL for WSDL (if one)
3710
     *
3711
     * @var mixed
3712
     * @access private
3713
     */
3714
    var $externalWSDLURL = false;
3715
    /**
3716
     * whether to append debug to response as XML comment
3717
     *
3718
     * @var boolean
3719
     * @access public
3720
     */
3721
    var $debug_flag = false;
3722
3723
    /** @var array */
3724
    var $opData;
3725
3726
3727
    /**
3728
     * constructor
3729
     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3730
     *
3731
     * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3732
     * @access   public
3733
     */
3734
    function __construct($wsdl = false)
3735
    {
3736
        parent::__construct();
3737
        // turn on debugging?
3738
        global $debug;
3739
        global $HTTP_SERVER_VARS;
3740
3741
        if (isset($_SERVER)) {
3742
            $this->debug("_SERVER is defined:");
3743
            $this->appendDebug($this->varDump($_SERVER));
3744
        } elseif (isset($HTTP_SERVER_VARS)) {
3745
            $this->debug("HTTP_SERVER_VARS is defined:");
3746
            $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3747
        } else {
3748
            $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3749
        }
3750
3751
        if (isset($debug)) {
3752
            $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3753
            $this->debug_flag = $debug;
3754
        } elseif (isset($_SERVER['QUERY_STRING'])) {
3755
            $qs = explode('&', $_SERVER['QUERY_STRING']);
3756
            foreach ($qs as $v) {
3757
                if (substr($v, 0, 6) == 'debug=') {
3758
                    $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3759
                    $this->debug_flag = substr($v, 6);
3760
                }
3761
            }
3762
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3763
            $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3764
            foreach ($qs as $v) {
3765
                if (substr($v, 0, 6) == 'debug=') {
3766
                    $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3767
                    $this->debug_flag = substr($v, 6);
3768
                }
3769
            }
3770
        }
3771
3772
        // wsdl
3773
        if ($wsdl) {
3774
            $this->debug("In nusoap_server, WSDL is specified");
3775
            if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3776
                $this->wsdl = $wsdl;
3777
                $this->externalWSDLURL = $this->wsdl->wsdl;
3778
                $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3779
            } else {
3780
                $this->debug('Create wsdl from ' . $wsdl);
3781
                $this->wsdl = new wsdl($wsdl);
3782
                $this->externalWSDLURL = $wsdl;
3783
            }
3784
            $this->appendDebug($this->wsdl->getDebug());
3785
            $this->wsdl->clearDebug();
3786
            if ($err = $this->wsdl->getError()) {
3787
                die('WSDL ERROR: ' . $err);
3788
            }
3789
        }
3790
    }
3791
3792
    /**
3793
     * processes request and returns response
3794
     *
3795
     * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3796
     * @access   public
3797
     */
3798
    function service($data)
3799
    {
3800
        global $HTTP_SERVER_VARS;
3801
3802
        if (isset($_SERVER['REQUEST_METHOD'])) {
3803
            $rm = $_SERVER['REQUEST_METHOD'];
3804
        } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
3805
            $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
3806
        } else {
3807
            $rm = '';
3808
        }
3809
3810
        if (isset($_SERVER['QUERY_STRING'])) {
3811
            $qs = $_SERVER['QUERY_STRING'];
3812
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3813
            $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3814
        } else {
3815
            $qs = '';
3816
        }
3817
        $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
3818
3819
        if ($rm == 'POST') {
3820
            $this->debug("In service, invoke the request");
3821
            $this->parse_request($data);
3822
            if (!$this->fault) {
3823
                $this->invoke_method();
3824
            }
3825
            if (!$this->fault) {
3826
                $this->serialize_return();
3827
            }
3828
            $this->send_response();
3829
        } elseif (preg_match('/wsdl/', $qs)) {
3830
            $this->debug("In service, this is a request for WSDL");
3831
            if ($this->externalWSDLURL) {
3832
                if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
3833
                    $this->debug("In service, re-direct for WSDL");
3834
                    header('Location: ' . $this->externalWSDLURL);
3835
                } else { // assume file
3836
                    $this->debug("In service, use file passthru for WSDL");
3837
                    header("Content-Type: text/xml\r\n");
3838
                    $fp = fopen($this->externalWSDLURL, 'r');
3839
                    fpassthru($fp);
3840
                }
3841
            } elseif ($this->wsdl) {
3842
                $this->debug("In service, serialize WSDL");
3843
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3844
                print $this->wsdl->serialize($this->debug_flag);
3845
                if ($this->debug_flag) {
3846
                    $this->debug('wsdl:');
3847
                    $this->appendDebug($this->varDump($this->wsdl));
3848
                    print $this->getDebugAsXMLComment();
3849
                }
3850
            } else {
3851
                $this->debug("In service, there is no WSDL");
3852
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3853
                print "This service does not provide WSDL";
3854
            }
3855
        } elseif ($this->wsdl) {
3856
            $this->debug("In service, return Web description");
3857
            print $this->wsdl->webDescription();
3858
        } else {
3859
            $this->debug("In service, no Web description");
3860
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3861
            print "This service does not provide a Web description";
3862
        }
3863
    }
3864
3865
    /**
3866
     * parses HTTP request headers.
3867
     *
3868
     * The following fields are set by this function (when successful)
3869
     *
3870
     * headers
3871
     * request
3872
     * xml_encoding
3873
     * SOAPAction
3874
     *
3875
     * @access   private
3876
     */
3877
    function parse_http_headers()
3878
    {
3879
        global $HTTP_SERVER_VARS;
3880
3881
        $this->request = '';
3882
        $this->SOAPAction = '';
3883
        if (function_exists('getallheaders')) {
3884
            $this->debug("In parse_http_headers, use getallheaders");
3885
            $headers = getallheaders();
3886
            foreach ($headers as $k => $v) {
3887
                $k = strtolower($k);
3888
                $this->headers[$k] = $v;
3889
                $this->request .= "$k: $v\r\n";
3890
                $this->debug("$k: $v");
3891
            }
3892
            // get SOAPAction header
3893
            if (isset($this->headers['soapaction'])) {
3894
                $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
3895
            }
3896
            // get the character encoding of the incoming request
3897
            if (isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) {
3898
                $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1));
3899
                if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3900
                    $this->xml_encoding = strtoupper($enc);
3901
                } else {
3902
                    $this->xml_encoding = 'US-ASCII';
3903
                }
3904
            } else {
3905
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3906
                $this->xml_encoding = 'ISO-8859-1';
3907
            }
3908
        } elseif (isset($_SERVER) && is_array($_SERVER)) {
3909
            $this->debug("In parse_http_headers, use _SERVER");
3910
            foreach ($_SERVER as $k => $v) {
3911
                if (substr($k, 0, 5) == 'HTTP_') {
3912
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3913
                } else {
3914
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3915
                }
3916
                if ($k == 'soapaction') {
3917
                    // get SOAPAction header
3918
                    $k = 'SOAPAction';
3919
                    $v = str_replace('"', '', $v);
3920
                    $v = str_replace('\\', '', $v);
3921
                    $this->SOAPAction = $v;
3922
                } elseif ($k == 'content-type') {
3923
                    // get the character encoding of the incoming request
3924
                    if (strpos($v, '=')) {
3925
                        $enc = substr(strstr($v, '='), 1);
3926
                        $enc = str_replace('"', '', $enc);
3927
                        $enc = str_replace('\\', '', $enc);
3928
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3929
                            $this->xml_encoding = strtoupper($enc);
3930
                        } else {
3931
                            $this->xml_encoding = 'US-ASCII';
3932
                        }
3933
                    } else {
3934
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3935
                        $this->xml_encoding = 'ISO-8859-1';
3936
                    }
3937
                }
3938
                $this->headers[$k] = $v;
3939
                if (is_array($v)) {
3940
                    $this->request .= "$k: " . json_encode($v) . "\r\n";
3941
                    $this->debug("$k: " . json_encode($v));
3942
                } else {
3943
                    $this->request .= "$k: $v\r\n";
3944
                    $this->debug("$k: $v");
3945
                }
3946
            }
3947
        } elseif (is_array($HTTP_SERVER_VARS)) {
3948
            $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3949
            foreach ($HTTP_SERVER_VARS as $k => $v) {
3950
                if (substr($k, 0, 5) == 'HTTP_') {
3951
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3952
                    $k = strtolower(substr($k, 5));
3953
                } else {
3954
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3955
                    $k = strtolower($k);
3956
                }
3957
                if ($k == 'soapaction') {
3958
                    // get SOAPAction header
3959
                    $k = 'SOAPAction';
3960
                    $v = str_replace('"', '', $v);
3961
                    $v = str_replace('\\', '', $v);
3962
                    $this->SOAPAction = $v;
3963
                } elseif ($k == 'content-type') {
3964
                    // get the character encoding of the incoming request
3965
                    if (strpos($v, '=')) {
3966
                        $enc = substr(strstr($v, '='), 1);
3967
                        $enc = str_replace('"', '', $enc);
3968
                        $enc = str_replace('\\', '', $enc);
3969
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3970
                            $this->xml_encoding = strtoupper($enc);
3971
                        } else {
3972
                            $this->xml_encoding = 'US-ASCII';
3973
                        }
3974
                    } else {
3975
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3976
                        $this->xml_encoding = 'ISO-8859-1';
3977
                    }
3978
                }
3979
                $this->headers[$k] = $v;
3980
                $this->request .= "$k: $v\r\n";
3981
                $this->debug("$k: $v");
3982
            }
3983
        } else {
3984
            $this->debug("In parse_http_headers, HTTP headers not accessible");
3985
            $this->setError("HTTP headers not accessible");
3986
        }
3987
    }
3988
3989
    /**
3990
     * parses a request
3991
     *
3992
     * The following fields are set by this function (when successful)
3993
     *
3994
     * headers
3995
     * request
3996
     * xml_encoding
3997
     * SOAPAction
3998
     * request
3999
     * requestSOAP
4000
     * methodURI
4001
     * methodname
4002
     * methodparams
4003
     * requestHeaders
4004
     * document
4005
     *
4006
     * This sets the fault field on error
4007
     *
4008
     * @param    string $data XML string
4009
     * @access   private
4010
     */
4011
    function parse_request($data = '')
4012
    {
4013
        $this->debug('entering parse_request()');
4014
        $this->parse_http_headers();
4015
        $this->debug('got character encoding: ' . $this->xml_encoding);
4016
        // uncompress if necessary
4017
        if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
4018
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
4019
            if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
4020
                // if decoding works, use it. else assume data wasn't gzencoded
4021
                if (function_exists('gzuncompress')) {
4022
                    if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
4023
                        $data = $degzdata;
4024
                    } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
4025
                        $data = $degzdata;
4026
                    } else {
4027
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
4028
                        return;
4029
                    }
4030
                } else {
4031
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
4032
                    return;
4033
                }
4034
            }
4035
        }
4036
        $this->request .= "\r\n" . $data;
4037
        $data = $this->parseRequest($this->headers, $data);
4038
        $this->requestSOAP = $data;
4039
        $this->debug('leaving parse_request');
4040
    }
4041
4042
    /**
4043
     * invokes a PHP function for the requested SOAP method
4044
     *
4045
     * The following fields are set by this function (when successful)
4046
     *
4047
     * methodreturn
4048
     *
4049
     * Note that the PHP function that is called may also set the following
4050
     * fields to affect the response sent to the client
4051
     *
4052
     * responseHeaders
4053
     * outgoing_headers
4054
     *
4055
     * This sets the fault field on error
4056
     *
4057
     * @access   private
4058
     */
4059
    function invoke_method()
4060
    {
4061
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
4062
4063
        //
4064
        // if you are debugging in this area of the code, your service uses a class to implement methods,
4065
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
4066
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
4067
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
4068
        // the XML request and reading the XML response.  you need to add the RequestElementName and
4069
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
4070
        // generates for the method.  these parameters are used to specify the correct XML element names
4071
        // for .NET to use, i.e. the names with the '.' in them.
4072
        //
4073
        $orig_methodname = $this->methodname;
4074
        if ($this->wsdl) {
4075
            if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
4076
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
4077
                $this->appendDebug('opData=' . $this->varDump($this->opData));
4078
            } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
4079
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
4080
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
4081
                $this->appendDebug('opData=' . $this->varDump($this->opData));
4082
                $this->methodname = $this->opData['name'];
4083
            } else {
4084
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
4085
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
4086
                return;
4087
            }
4088
        } else {
4089
            $this->debug('in invoke_method, no WSDL to validate method');
4090
        }
4091
4092
        // if a . is present in $this->methodname, we see if there is a class in scope,
4093
        // which could be referred to. We will also distinguish between two deliminators,
4094
        // to allow methods to be called a the class or an instance
4095
        if (strpos($this->methodname, '..') > 0) {
4096
            $delim = '..';
4097
        } elseif (strpos($this->methodname, '.') > 0) {
4098
            $delim = '.';
4099
        } else {
4100
            $delim = '';
4101
        }
4102
        $this->debug("in invoke_method, delim=$delim");
4103
4104
        $class = '';
4105
        $method = '';
4106
        $try_class = '';
4107
        if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
4108
            $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
4109
            if (class_exists($try_class)) {
4110
                // get the class and method name
4111
                $class = $try_class;
4112
                $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
4113
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4114
            } else {
4115
                $this->debug("in invoke_method, class=$try_class not found");
4116
            }
4117
        } elseif (strlen($delim) > 0 && substr_count($this->methodname, $delim) > 1) {
4118
            $split = explode($delim, $this->methodname);
4119
            $method = array_pop($split);
4120
            $class = implode('\\', $split);
4121
        } else {
4122
            $this->debug("in invoke_method, no class to try");
4123
        }
4124
4125
        // does method exist?
4126
        if ($class == '') {
4127
            if (!function_exists($this->methodname)) {
4128
                $this->debug("in invoke_method, function '$this->methodname' not found!");
4129
                $this->result = 'fault: method not found';
4130
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
4131
                return;
4132
            }
4133
        } else {
4134
            $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
4135
            if (!in_array($method_to_compare, get_class_methods($class))) {
4136
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4137
                $this->result = 'fault: method not found';
4138
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4139
                return;
4140
            }
4141
        }
4142
4143
        // evaluate message, getting back parameters
4144
        // verify that request parameters match the method's signature
4145
        if (!$this->verify_method($this->methodname, $this->methodparams)) {
4146
            // debug
4147
            $this->debug('ERROR: request not verified against method signature');
4148
            $this->result = 'fault: request failed validation against method signature';
4149
            // return fault
4150
            $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
4151
            return;
4152
        }
4153
4154
        // if there are parameters to pass
4155
        $this->debug('in invoke_method, params:');
4156
        $this->appendDebug($this->varDump($this->methodparams));
4157
        $this->debug("in invoke_method, calling '$this->methodname'");
4158
        if (!function_exists('call_user_func_array')) {
4159
            if ($class == '') {
4160
                $this->debug('in invoke_method, calling function using eval()');
4161
                $funcCall = "\$this->methodreturn = $this->methodname(";
4162
            } else {
4163
                if ($delim == '..') {
4164
                    $this->debug('in invoke_method, calling class method using eval()');
4165
                    $funcCall = "\$this->methodreturn = " . $class . "::" . $method . "(";
4166
                } else {
4167
                    $this->debug('in invoke_method, calling instance method using eval()');
4168
                    // generate unique instance name
4169
                    $instname = "\$inst_" . time();
4170
                    $funcCall = $instname . " = new " . $class . "(); ";
4171
                    $funcCall .= "\$this->methodreturn = " . $instname . "->" . $method . "(";
4172
                }
4173
            }
4174
            if ($this->methodparams) {
4175
                foreach ($this->methodparams as $param) {
4176
                    if (is_array($param) || is_object($param)) {
4177
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4178
                        return;
4179
                    }
4180
                    $funcCall .= "\"$param\",";
4181
                }
4182
                $funcCall = substr($funcCall, 0, -1);
4183
            }
4184
            $funcCall .= ');';
4185
            $this->debug('in invoke_method, function call: ' . $funcCall);
4186
            @eval($funcCall);
4187
        } else {
4188
            if ($class == '') {
4189
                $this->debug('in invoke_method, calling function using call_user_func_array()');
4190
                $call_arg = "$this->methodname";    // straight assignment changes $this->methodname to lower case after call_user_func_array()
4191
            } elseif ($delim == '..') {
4192
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
4193
                $call_arg = array($class, $method);
4194
            } else {
4195
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4196
                $instance = new $class();
4197
                $call_arg = array(&$instance, $method);
4198
            }
4199
            if (is_array($this->methodparams)) {
4200
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4201
            } else {
4202
                $this->methodreturn = call_user_func_array($call_arg, array());
4203
            }
4204
        }
4205
        $this->debug('in invoke_method, methodreturn:');
4206
        $this->appendDebug($this->varDump($this->methodreturn));
4207
        $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
4208
    }
4209
4210
    /**
4211
     * serializes the return value from a PHP function into a full SOAP Envelope
4212
     *
4213
     * The following fields are set by this function (when successful)
4214
     *
4215
     * responseSOAP
4216
     *
4217
     * This sets the fault field on error
4218
     *
4219
     * @access   private
4220
     */
4221
    function serialize_return()
4222
    {
4223
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4224
        // if fault
4225
        if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
4226
            $this->debug('got a fault object from method');
4227
            $this->fault = $this->methodreturn;
4228
            return;
4229
        } elseif ($this->methodreturnisliteralxml) {
4230
            $return_val = $this->methodreturn;
4231
            // returned value(s)
4232
        } else {
4233
            $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
4234
            $this->debug('serializing return value');
4235
            if ($this->wsdl) {
4236
                if (sizeof($this->opData['output']['parts']) > 1) {
4237
                    $this->debug('more than one output part, so use the method return unchanged');
4238
                    $opParams = $this->methodreturn;
4239
                } elseif (sizeof($this->opData['output']['parts']) == 1) {
4240
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
4241
                    // TODO: verify that it is not already wrapped!
4242
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
4243
                    //  $this->debug('wrap in element named ' . $name);
4244
                    //}
4245
                    $opParams = array($this->methodreturn);
4246
                }
4247
                $opParams = isset($opParams) ? $opParams : [];
4248
                $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams);
4249
                $this->appendDebug($this->wsdl->getDebug());
4250
                $this->wsdl->clearDebug();
4251
                if ($errstr = $this->wsdl->getError()) {
4252
                    $this->debug('got wsdl error: ' . $errstr);
4253
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4254
                    return;
4255
                }
4256
            } else {
4257
                if (isset($this->methodreturn)) {
4258
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
4259
                } else {
4260
                    $return_val = '';
4261
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
4262
                }
4263
            }
4264
        }
4265
        $this->debug('return value:');
4266
        $this->appendDebug($this->varDump($return_val));
4267
4268
        $this->debug('serializing response');
4269
        if ($this->wsdl) {
4270
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4271
            if ($this->opData['style'] == 'rpc') {
4272
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4273
                if ($this->opData['output']['use'] == 'literal') {
4274
                    // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
4275
                    if ($this->methodURI) {
4276
                        $payload = '<ns1:' . $this->responseTagName . ' xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->responseTagName . ">";
4277
                    } else {
4278
                        $payload = '<' . $this->responseTagName . '>' . $return_val . '</' . $this->responseTagName . 'Response>';
4279
                    }
4280
                } else {
4281
                    if ($this->methodURI) {
4282
                        $payload = '<ns1:' . $this->responseTagName . ' xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->responseTagName . ">";
4283
                    } else {
4284
                        $payload = '<' . $this->responseTagName . '>' . $return_val . '</' . $this->responseTagName . '>';
4285
                    }
4286
                }
4287
            } else {
4288
                $this->debug('style is not rpc for serialization: assume document');
4289
                $payload = $return_val;
4290
            }
4291
        } else {
4292
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4293
            $payload = '<ns1:' . $this->responseTagName . ' xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->responseTagName . ">";
4294
        }
4295
        $this->result = 'successful';
4296
        if ($this->wsdl) {
4297
            //if($this->debug_flag){
4298
            $this->appendDebug($this->wsdl->getDebug());
4299
            //  }
4300
            if (isset($this->opData['output']['encodingStyle'])) {
4301
                $encodingStyle = $this->opData['output']['encodingStyle'];
4302
            } else {
4303
                $encodingStyle = '';
4304
            }
4305
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4306
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
4307
        } else {
4308
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
4309
        }
4310
        $this->debug("Leaving serialize_return");
4311
    }
4312
4313
    /**
4314
     * sends an HTTP response
4315
     *
4316
     * The following fields are set by this function (when successful)
4317
     *
4318
     * outgoing_headers
4319
     * response
4320
     *
4321
     * @access   private
4322
     */
4323
    function send_response()
4324
    {
4325
        $this->debug('Enter send_response');
4326
        if ($this->fault) {
4327
            $payload = $this->fault->serialize();
4328
            $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
4329
            $this->outgoing_headers[] = "Status: 500 Internal Server Error";
4330
        } else {
4331
            $payload = $this->responseSOAP;
4332
            // Some combinations of PHP+Web server allow the Status
4333
            // to come through as a header.  Since OK is the default
4334
            // just do nothing.
4335
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4336
            // $this->outgoing_headers[] = "Status: 200 OK";
4337
        }
4338
        // add debug data if in debug mode
4339
        if (isset($this->debug_flag) && $this->debug_flag) {
4340
            $payload .= $this->getDebugAsXMLComment();
4341
        }
4342
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4343
        $rev = array();
4344
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4345
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . (isset($rev[1]) ? $rev[1] : '') . ")";
4346
        // Let the Web server decide about this
4347
        //$this->outgoing_headers[] = "Connection: Close\r\n";
4348
        $payload = $this->getHTTPBody($payload);
4349
        $type = $this->getHTTPContentType();
4350
        $charset = $this->getHTTPContentTypeCharset();
4351
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4352
        //begin code to compress payload - by John
4353
        // NOTE: there is no way to know whether the Web server will also compress
4354
        // this data.
4355
        if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4356
            if (strstr($this->headers['accept-encoding'], 'gzip')) {
4357
                if (function_exists('gzencode')) {
4358
                    if (isset($this->debug_flag) && $this->debug_flag) {
4359
                        $payload .= "<!-- Content being gzipped -->";
4360
                    }
4361
                    $this->outgoing_headers[] = "Content-Encoding: gzip";
4362
                    $payload = gzencode($payload);
4363
                } else {
4364
                    if (isset($this->debug_flag) && $this->debug_flag) {
4365
                        $payload .= "<!-- Content will not be gzipped: no gzencode -->";
4366
                    }
4367
                }
4368
            } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
4369
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4370
                // instead of gzcompress output,
4371
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4372
                if (function_exists('gzdeflate')) {
4373
                    if (isset($this->debug_flag) && $this->debug_flag) {
4374
                        $payload .= "<!-- Content being deflated -->";
4375
                    }
4376
                    $this->outgoing_headers[] = "Content-Encoding: deflate";
4377
                    $payload = gzdeflate($payload);
4378
                } else {
4379
                    if (isset($this->debug_flag) && $this->debug_flag) {
4380
                        $payload .= "<!-- Content will not be deflated: no gzcompress -->";
4381
                    }
4382
                }
4383
            }
4384
        }
4385
        //end code
4386
        $this->outgoing_headers[] = "Content-Length: " . strlen($payload);
4387
        reset($this->outgoing_headers);
4388
        foreach ($this->outgoing_headers as $hdr) {
4389
            header($hdr, false);
4390
        }
4391
        print $payload;
4392
        $this->response = join("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
4393
    }
4394
4395
    /**
4396
     * takes the value that was created by parsing the request
4397
     * and compares to the method's signature, if available.
4398
     *
4399
     * @param    string $operation The operation to be invoked
4400
     * @param    array $request The array of parameter values
4401
     * @return    boolean    Whether the operation was found
4402
     * @access   private
4403
     */
4404
    function verify_method($operation, $request)
4405
    {
4406
        if (isset($this->wsdl) && is_object($this->wsdl)) {
4407
            if ($this->wsdl->getOperationData($operation)) {
4408
                return true;
4409
            }
4410
        } elseif (isset($this->operations[$operation])) {
4411
            return true;
4412
        }
4413
        return false;
4414
    }
4415
4416
    /**
4417
     * processes SOAP message received from client
4418
     *
4419
     * @param    array $headers The HTTP headers
4420
     * @param    string $data unprocessed request data from client
4421
     * @return   false|void void or false on error
4422
     * @access   private
4423
     */
4424
    function parseRequest($headers, $data)
4425
    {
4426
        $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
4427
        $this->appendDebug($this->varDump($headers));
4428
        if (!isset($headers['content-type'])) {
4429
            $this->setError('Request not of type ' . $this->contentType . ' (no content-type header)');
4430
            return false;
4431
        }
4432
        if (!strstr($headers['content-type'], $this->contentType)) {
4433
            $this->setError('Request not of type ' . $this->contentType . ': ' . $headers['content-type']);
4434
            return false;
4435
        }
4436
        if (strpos($headers['content-type'], '=')) {
4437
            $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
4438
            $this->debug('Got response encoding: ' . $enc);
4439
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
4440
                $this->xml_encoding = strtoupper($enc);
4441
            } else {
4442
                $this->xml_encoding = 'US-ASCII';
4443
            }
4444
        } else {
4445
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4446
            $this->xml_encoding = 'ISO-8859-1';
4447
        }
4448
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4449
        // parse response, get soap parser obj
4450
        $parser = new nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
4451
        // parser debug
4452
        $this->debug("parser debug: \n" . $parser->getDebug());
4453
        // if fault occurred during message parsing
4454
        if ($err = $parser->getError()) {
4455
            $this->result = 'fault: error in msg parsing: ' . $err;
4456
            $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
4457
            // else successfully parsed request into soapval object
4458
        } else {
4459
            // get/set methodname
4460
            $this->methodURI = $parser->root_struct_namespace;
4461
            $this->methodname = $parser->root_struct_name;
4462
            $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4463
4464
            // get/set custom response tag name
4465
            $outputMessage = $this->wsdl->getOperationData($this->methodname)['output']['message'];
4466
            $this->responseTagName = $outputMessage;
4467
            $this->debug('responseTagName: ' . $this->responseTagName . ' methodURI: ' . $this->methodURI);
4468
4469
            $this->debug('calling parser->get_soapbody()');
4470
            $this->methodparams = $parser->get_soapbody();
4471
            // get SOAP headers
4472
            $this->requestHeaders = $parser->getHeaders();
4473
            // get SOAP Header
4474
            $this->requestHeader = $parser->get_soapheader();
4475
            // add document for doclit support
4476
            $this->document = $parser->document;
4477
        }
4478
    }
4479
4480
    /**
4481
     * gets the HTTP body for the current response.
4482
     *
4483
     * @param string $soapmsg The SOAP payload
4484
     * @return string The HTTP body, which includes the SOAP payload
4485
     * @access private
4486
     */
4487
    function getHTTPBody($soapmsg)
4488
    {
4489
        return $soapmsg;
4490
    }
4491
4492
    /**
4493
     * gets the HTTP content type for the current response.
4494
     *
4495
     * Note: getHTTPBody must be called before this.
4496
     *
4497
     * @return string the HTTP content type for the current response.
4498
     * @access private
4499
     */
4500
    function getHTTPContentType()
4501
    {
4502
        return 'text/xml';
4503
    }
4504
4505
    /**
4506
     * gets the HTTP content type charset for the current response.
4507
     * returns false for non-text content types.
4508
     *
4509
     * Note: getHTTPBody must be called before this.
4510
     *
4511
     * @return string the HTTP content type charset for the current response.
4512
     * @access private
4513
     */
4514
    function getHTTPContentTypeCharset()
4515
    {
4516
        return $this->soap_defencoding;
4517
    }
4518
4519
    /**
4520
     * add a method to the dispatch map (this has been replaced by the register method)
4521
     *
4522
     * @param    string $methodname
4523
     * @param    string $in array of input values
4524
     * @param    string $out array of output values
4525
     * @access   public
4526
     * @deprecated
4527
     */
4528
    function add_to_map($methodname, $in, $out)
4529
    {
4530
        $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out);
4531
    }
4532
4533
    /**
4534
     * register a service function with the server
4535
     *
4536
     * @param    string $name the name of the PHP function, class.method or class..method
4537
     * @param    array $in assoc array of input values: key = param name, value = param type
4538
     * @param    array $out assoc array of output values: key = param name, value = param type
4539
     * @param    mixed $namespace the element namespace for the method or false
4540
     * @param    mixed $soapaction the soapaction for the method or false
4541
     * @param    mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4542
     * @param    mixed $use optional (encoded|literal) or false
4543
     * @param    string $documentation optional Description to include in WSDL
4544
     * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4545
     * @param    string $customResponseTagName optional Name of the outgoing response, default $name . 'Response'
4546
     * @access   public
4547
     */
4548
    function register($name, $in = array(), $out = array(), $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '', $customResponseTagName = '')
4549
    {
4550
        global $HTTP_SERVER_VARS;
4551
4552
        if ($this->externalWSDLURL) {
4553
            die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
4554
        }
4555
        if (!$name) {
4556
            die('You must specify a name when you register an operation');
4557
        }
4558
        if (!is_array($in)) {
4559
            die('You must provide an array for operation inputs');
4560
        }
4561
        if (!is_array($out)) {
4562
            die('You must provide an array for operation outputs');
4563
        }
4564
        if (!$soapaction) {
4565
            if (isset($_SERVER)) {
4566
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
4567
                $SCRIPT_NAME = $_SERVER['SCRIPT_NAME'];
4568
                $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4569
            } elseif (isset($HTTP_SERVER_VARS)) {
4570
                $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4571
                $SCRIPT_NAME = $HTTP_SERVER_VARS['SCRIPT_NAME'];
4572
                $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4573
            } else {
4574
                $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4575
                $HTTPS = '';
4576
                $SERVER_NAME = '';
4577
                $SCRIPT_NAME = '';
4578
            }
4579
            if ($HTTPS == '1' || $HTTPS == 'on') {
4580
                $SCHEME = 'https';
4581
            } else {
4582
                $SCHEME = 'http';
4583
            }
4584
            $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
4585
        }
4586
        if (!$style) {
4587
            $style = "rpc";
4588
        }
4589
        if (!$use) {
4590
            $use = "encoded";
4591
        }
4592
        if ($use == 'encoded' && $encodingStyle == '') {
4593
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4594
        }
4595
        if (!$customResponseTagName) {
4596
            $customResponseTagName = $name . 'Response';
4597
        }
4598
4599
        $this->operations[$name] = array(
4600
            'name' => $name,
4601
            'in' => $in,
4602
            'out' => $out,
4603
            'namespace' => $namespace,
4604
            'soapaction' => $soapaction,
4605
            'style' => $style,
4606
            'outputMessage' => $customResponseTagName,
4607
        );
4608
        if ($this->wsdl) {
4609
            $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle, $customResponseTagName);
4610
        }
4611
        return true;
4612
    }
4613
4614
    /**
4615
     * Specify a fault to be returned to the client.
4616
     * This also acts as a flag to the server that a fault has occured.
4617
     *
4618
     * @param    string $faultcode
4619
     * @param    string $faultstring
4620
     * @param    string $faultactor
4621
     * @param    string $faultdetail
4622
     * @access   public
4623
     */
4624
    function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
4625
    {
4626
        if ($faultdetail == '' && $this->debug_flag) {
4627
            $faultdetail = $this->getDebug();
4628
        }
4629
        $this->fault = new nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
4630
        $this->fault->soap_defencoding = $this->soap_defencoding;
4631
    }
4632
4633
    /**
4634
     * Sets up wsdl object.
4635
     * Acts as a flag to enable internal WSDL generation
4636
     *
4637
     * @param string $serviceName , name of the service
4638
     * @param mixed $namespace optional 'tns' service namespace or false
4639
     * @param mixed $endpoint optional URL of service endpoint or false
4640
     * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4641
     * @param string $transport optional SOAP transport
4642
     * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4643
     */
4644
    function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4645
    {
4646
        global $HTTP_SERVER_VARS;
4647
4648
        if (isset($_SERVER)) {
4649
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
4650
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
4651
            $SCRIPT_NAME = $_SERVER['SCRIPT_NAME'];
4652
            $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4653
        } elseif (isset($HTTP_SERVER_VARS)) {
4654
            $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4655
            $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4656
            $SCRIPT_NAME = $HTTP_SERVER_VARS['SCRIPT_NAME'];
4657
            $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4658
        } else {
4659
            $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4660
            $SERVER_PORT = '';
4661
            $SERVER_NAME = '';
4662
            $SCRIPT_NAME = '';
4663
            $HTTPS = '';
4664
        }
4665
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4666
        $colon = strpos($SERVER_NAME, ":");
4667
        if ($colon) {
4668
            $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
4669
        }
4670
        if ($SERVER_PORT == 80) {
4671
            $SERVER_PORT = '';
4672
        } else {
4673
            $SERVER_PORT = ':' . $SERVER_PORT;
4674
        }
4675
        if (!$namespace) {
4676
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4677
        }
4678
4679
        if (!$endpoint) {
4680
            if ($HTTPS == '1' || $HTTPS == 'on') {
4681
                $SCHEME = 'https';
4682
            } else {
4683
                $SCHEME = 'http';
4684
            }
4685
            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4686
        }
4687
4688
        if (!$schemaTargetNamespace) {
4689
            $schemaTargetNamespace = $namespace;
4690
        }
4691
4692
        $this->wsdl = new wsdl();
4693
        $this->wsdl->serviceName = $serviceName;
4694
        $this->wsdl->endpoint = $endpoint;
4695
        $this->wsdl->namespaces['tns'] = $namespace;
4696
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4697
        $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4698
        if ($schemaTargetNamespace != $namespace) {
4699
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4700
        }
4701
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
4702
        if ($style == 'document') {
4703
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4704
        }
4705
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4706
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4707
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4708
        $this->wsdl->bindings[$serviceName . 'Binding'] = array(
4709
            'name' => $serviceName . 'Binding',
4710
            'style' => $style,
4711
            'transport' => $transport,
4712
            'portType' => $serviceName . 'PortType');
4713
        $this->wsdl->ports[$serviceName . 'Port'] = array(
4714
            'binding' => $serviceName . 'Binding',
4715
            'location' => $endpoint,
4716
            'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/');
4717
    }
4718
}
4719
4720
/**
4721
 * Backward compatibility
4722
 */
4723
class soap_server extends nusoap_server
4724
{
4725
}
4726
4727
4728
/**
4729
 * parses a WSDL file, allows access to it's data, other utility methods.
4730
 * also builds WSDL structures programmatically.
4731
 *
4732
 * @author   Dietrich Ayala <[email protected]>
4733
 * @author   Scott Nichol <[email protected]>
4734
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
4735
 * @access public
4736
 */
4737
class wsdl extends nusoap_base
4738
{
4739
    // URL or filename of the root of this WSDL
4740
    var $wsdl;
4741
    // define internal arrays of bindings, ports, operations, messages, etc.
4742
    var $schemas = array();
4743
    var $currentSchema;
4744
    var $message = array();
4745
    var $complexTypes = array();
4746
    var $messages = array();
4747
    var $currentMessage;
4748
    var $currentOperation;
4749
    var $portTypes = array();
4750
    var $currentPortType;
4751
    var $bindings = array();
4752
    var $currentBinding;
4753
    var $ports = array();
4754
    var $currentPort;
4755
    var $opData = array();
4756
    var $status = '';
4757
    var $documentation = false;
4758
    var $endpoint = '';
4759
    // array of wsdl docs to import
4760
    var $import = array();
4761
    // parser vars
4762
    var $parser;
4763
    var $position = 0;
4764
    var $depth = 0;
4765
    var $depth_array = array();
4766
    // for getting wsdl
4767
    var $proxyhost = '';
4768
    var $proxyport = '';
4769
    var $proxyusername = '';
4770
    var $proxypassword = '';
4771
    var $timeout = 0;
4772
    var $response_timeout = 30;
4773
    var $curl_options = array();    // User-specified cURL options
4774
    var $use_curl = false;            // whether to always try to use cURL
4775
    // for HTTP authentication
4776
    var $username = '';                // Username for HTTP authentication
4777
    var $password = '';                // Password for HTTP authentication
4778
    var $authtype = '';                // Type of HTTP authentication
4779
    var $certRequest = array();        // Certificate for HTTP SSL authentication
4780
4781
    /** @var mixed */
4782
    var $currentPortOperation;
4783
    /** @var string */
4784
    var $opStatus;
4785
    /** @var mixed */
4786
    var $serviceName;
4787
    var $wsdl_info;
4788
4789
    /**
4790
     * constructor
4791
     *
4792
     * @param string $wsdl WSDL document URL
4793
     * @param string $proxyhost
4794
     * @param string $proxyport
4795
     * @param string $proxyusername
4796
     * @param string $proxypassword
4797
     * @param integer $timeout set the connection timeout
4798
     * @param integer $response_timeout set the response timeout
4799
     * @param array $curl_options user-specified cURL options
4800
     * @param boolean $use_curl try to use cURL
4801
     * @access public
4802
     */
4803
    function __construct($wsdl = '', $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $curl_options = null, $use_curl = false)
4804
    {
4805
        parent::__construct();
4806
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4807
        $this->proxyhost = $proxyhost;
4808
        $this->proxyport = $proxyport;
4809
        $this->proxyusername = $proxyusername;
4810
        $this->proxypassword = $proxypassword;
4811
        $this->timeout = $timeout;
4812
        $this->response_timeout = $response_timeout;
4813
        if (is_array($curl_options)) {
4814
            $this->curl_options = $curl_options;
4815
        }
4816
        $this->use_curl = $use_curl;
4817
        $this->fetchWSDL($wsdl);
4818
    }
4819
4820
    /**
4821
     * fetches the WSDL document and parses it
4822
     *
4823
     * @access public
4824
     */
4825
    function fetchWSDL($wsdl)
4826
    {
4827
        $this->debug("parse and process WSDL path=$wsdl");
4828
        $this->wsdl = $wsdl;
4829
        // parse wsdl file
4830
        if ($this->wsdl != "") {
4831
            $this->parseWSDL($this->wsdl);
4832
        }
4833
        // imports
4834
        // TODO: handle imports more properly, grabbing them in-line and nesting them
4835
        $imported_urls = array();
4836
        $imported = 1;
4837
        while ($imported > 0) {
4838
            $imported = 0;
4839
            // Schema imports
4840
            foreach ($this->schemas as $ns => $list) {
4841
                foreach ($list as $xsKey => $xs) {
4842
                    $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4843
                    foreach ($xs->imports as $ns2 => $list2) {
4844
                        for ($ii = 0; $ii < count($list2); $ii++) {
4845
                            if (array_key_exists($ii, $list2) && (!isset($list2[$ii]['loaded']) || !$list2[$ii]['loaded'])) {
4846
                                @$this->schemas[$ns][$xsKey]->imports[$ns2][$ii]['loaded'] = true;
4847
                                $url = $list2[$ii]['location'];
4848
                                if ($url != '') {
4849
                                    $urlparts = parse_url($url);
4850
                                    if (!isset($urlparts['host'])) {
4851
                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4852
                                            substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4853
                                    }
4854
                                    if (!in_array($url, $imported_urls)) {
4855
                                        $this->parseWSDL($url);
4856
                                        $imported++;
4857
                                        $imported_urls[] = $url;
4858
                                    }
4859
                                } else {
4860
                                    $this->debug("Unexpected scenario: empty URL for unloaded import");
4861
                                }
4862
                            }
4863
                        }
4864
                    }
4865
                }
4866
            }
4867
            // WSDL imports
4868
            $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4869
            foreach ($this->import as $ns => $list) {
4870
                for ($ii = 0; $ii < count($list); $ii++) {
4871
                    if (!$list[$ii]['loaded']) {
4872
                        $this->import[$ns][$ii]['loaded'] = true;
4873
                        $url = $list[$ii]['location'];
4874
                        if ($url != '') {
4875
                            $urlparts = parse_url($url);
4876
                            if (!isset($urlparts['host'])) {
4877
                                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4878
                                    substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4879
                            }
4880
                            if (!in_array($url, $imported_urls)) {
4881
                                $this->parseWSDL($url);
4882
                                $imported++;
4883
                                $imported_urls[] = $url;
4884
                            }
4885
                        } else {
4886
                            $this->debug("Unexpected scenario: empty URL for unloaded import");
4887
                        }
4888
                    }
4889
                }
4890
            }
4891
        }
4892
        // add new data to operation data
4893
        foreach ($this->bindings as $binding => $bindingData) {
4894
            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4895
                foreach ($bindingData['operations'] as $operation => $data) {
4896
                    $this->debug('post-parse data gathering for ' . $operation);
4897
                    $this->bindings[$binding]['operations'][$operation]['input'] =
4898
                        isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4899
                            array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) :
4900
                            $this->portTypes[$bindingData['portType']][$operation]['input'];
4901
                    $this->bindings[$binding]['operations'][$operation]['output'] =
4902
                        isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4903
                            array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) :
4904
                            $this->portTypes[$bindingData['portType']][$operation]['output'];
4905
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
4906
                        $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
4907
                    }
4908
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
4909
                        $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
4910
                    }
4911
                    // Set operation style if necessary, but do not override one already provided
4912
                    if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4913
                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4914
                    }
4915
                    $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4916
                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
4917
                    $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4918
                }
4919
            }
4920
        }
4921
    }
4922
4923
    /**
4924
     * parses the wsdl document
4925
     *
4926
     * @param string $wsdl path or URL
4927
     * @access private
4928
     */
4929
    function parseWSDL($wsdl = '')
4930
    {
4931
        $this->debug("parse WSDL at path=$wsdl");
4932
4933
        if ($wsdl == '') {
4934
            $this->debug('no wsdl passed to parseWSDL()!!');
4935
            $this->setError('no wsdl passed to parseWSDL()!!');
4936
            return false;
4937
        }
4938
4939
        // parse $wsdl for url format
4940
        $wsdl_props = parse_url($wsdl);
4941
4942
        if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4943
            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4944
            // get wsdl
4945
            $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4946
            $tr->request_method = 'GET';
4947
            $tr->useSOAPAction = false;
4948
            if ($this->proxyhost && $this->proxyport) {
4949
                $tr->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
4950
            }
4951
            if ($this->authtype != '') {
4952
                $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
4953
            }
4954
            $tr->setEncoding();
4955
            $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4956
            //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4957
            //$this->debug("WSDL response\n" . $tr->incoming_payload);
4958
            $this->appendDebug($tr->getDebug());
4959
            // catch errors
4960
            if ($err = $tr->getError()) {
4961
                $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: ' . $err;
4962
                $this->debug($errstr);
4963
                $this->setError($errstr);
4964
                unset($tr);
4965
                return false;
4966
            }
4967
            unset($tr);
4968
            $this->debug("got WSDL URL");
4969
        } else {
4970
            // $wsdl is not http(s), so treat it as a file URL or plain file path
4971
            if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4972
                $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4973
            } else {
4974
                $path = $wsdl;
4975
            }
4976
            $this->debug('getting WSDL file ' . $path);
4977
            if ($fp = @fopen($path, 'r')) {
4978
                $wsdl_string = '';
4979
                while ($data = fread($fp, 32768)) {
4980
                    $wsdl_string .= $data;
4981
                }
4982
                fclose($fp);
4983
            } else {
4984
                $errstr = "Bad path to WSDL file $path";
4985
                $this->debug($errstr);
4986
                $this->setError($errstr);
4987
                return false;
4988
            }
4989
        }
4990
        $this->debug('Parse WSDL');
4991
        // end new code added
4992
        // Create an XML parser.
4993
        $this->parser = xml_parser_create();
4994
        // Set the options for parsing the XML data.
4995
        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4996
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4997
        // Set the object for the parser.
4998
        xml_set_object($this->parser, $this);
4999
        // Set the element handlers for the parser.
5000
        xml_set_element_handler($this->parser, 'start_element', 'end_element');
5001
        xml_set_character_data_handler($this->parser, 'character_data');
5002
        // Parse the XML file.
5003
        if (!xml_parse($this->parser, $wsdl_string, true)) {
5004
            // Display an error message.
5005
            $errstr = sprintf(
5006
                'XML error parsing WSDL from %s on line %d: %s',
5007
                $wsdl,
5008
                xml_get_current_line_number($this->parser),
5009
                xml_error_string(xml_get_error_code($this->parser))
5010
            );
5011
            $this->debug($errstr);
5012
            $this->debug("XML payload:\n" . $wsdl_string);
5013
            $this->setError($errstr);
5014
            xml_parser_free($this->parser);
5015
            unset($this->parser);
5016
            return false;
5017
        }
5018
        // free the parser
5019
        xml_parser_free($this->parser);
5020
        unset($this->parser);
5021
        $this->debug('Parsing WSDL done');
5022
        // catch wsdl parse errors
5023
        if ($this->getError()) {
5024
            return false;
5025
        }
5026
        return true;
5027
    }
5028
5029
    /**
5030
     * start-element handler
5031
     *
5032
     * @param string $parser XML parser object
5033
     * @param string $name element name
5034
     * @param array $attrs associative array of attributes
5035
     * @access private
5036
     */
5037
    function start_element($parser, $name, $attrs)
5038
    {
5039
        if ($this->status == 'schema') {
5040
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
5041
            $this->appendDebug($this->currentSchema->getDebug());
5042
            $this->currentSchema->clearDebug();
5043
        } elseif (preg_match('/schema$/', $name)) {
5044
            $this->debug('Parsing WSDL schema');
5045
            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
5046
            $this->status = 'schema';
5047
            $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
5048
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
5049
            $this->appendDebug($this->currentSchema->getDebug());
5050
            $this->currentSchema->clearDebug();
5051
        } else {
5052
            // position in the total number of elements, starting from 0
5053
            $pos = $this->position++;
5054
            $depth = $this->depth++;
5055
            // set self as current value for this depth
5056
            $this->depth_array[$depth] = $pos;
5057
            $this->message[$pos] = array('cdata' => '');
5058
            // process attributes
5059
            if (count($attrs) > 0) {
5060
                // register namespace declarations
5061
                foreach ($attrs as $k => $v) {
5062
                    if (preg_match('/^xmlns/', $k)) {
5063
                        if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
5064
                            $this->namespaces[$ns_prefix] = $v;
5065
                        } else {
5066
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
5067
                        }
5068
                        if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
5069
                            $this->XMLSchemaVersion = $v;
5070
                            $this->namespaces['xsi'] = $v . '-instance';
5071
                        }
5072
                    }
5073
                }
5074
                // expand each attribute prefix to its namespace
5075
                $eAttrs = array ();
5076
                foreach ($attrs as $k => $v) {
5077
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
5078
                    if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
5079
                        $v = strpos($v, ':') ? $this->expandQname($v) : $v;
5080
                    }
5081
                    $eAttrs[$k] = $v;
5082
                }
5083
                $attrs = $eAttrs;
5084
            } else {
5085
                $attrs = array();
5086
            }
5087
        // Set default prefix and namespace
5088
        // to prevent error Undefined variable $prefix and $namespace if (preg_match('/:/', $name)) return 0 or FALSE
5089
        $prefix = '';
5090
        $namespace = '';
5091
            // get element prefix, namespace and name
5092
            if (preg_match('/:/', $name)) {
5093
                // get ns prefix
5094
                $prefix = substr($name, 0, strpos($name, ':'));
5095
                // get ns
5096
                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
5097
                // get unqualified name
5098
                $name = substr(strstr($name, ':'), 1);
5099
            }
5100
            // process attributes, expanding any prefixes to namespaces
5101
            // find status, register data
5102
            switch ($this->status) {
5103
                case 'message':
5104
                    if ($name == 'part') {
5105
                        if (isset($attrs['type'])) {
5106
                            $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
5107
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
5108
                        }
5109
                        if (isset($attrs['element'])) {
5110
                            $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
5111
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
5112
                        }
5113
                    }
5114
                    break;
5115
                case 'portType':
5116
                    switch ($name) {
5117
                        case 'operation':
5118
                            $this->currentPortOperation = $attrs['name'];
5119
                            $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
5120
                            if (isset($attrs['parameterOrder'])) {
5121
                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
5122
                            }
5123
                            break;
5124
                        case 'documentation':
5125
                            $this->documentation = true;
5126
                            break;
5127
                        // merge input/output data
5128
                        default:
5129
                            $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
5130
                            $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
5131
                            break;
5132
                    }
5133
                    break;
5134
                case 'binding':
5135
                    switch ($name) {
5136
                        case 'binding':
5137
                            // get ns prefix
5138
                            if (isset($attrs['style'])) {
5139
                                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
5140
                            }
5141
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
5142
                            break;
5143
                        case 'header':
5144
                            $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
5145
                            break;
5146
                        case 'operation':
5147
                            if (isset($attrs['soapAction'])) {
5148
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
5149
                            }
5150
                            if (isset($attrs['style'])) {
5151
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
5152
                            }
5153
                            if (isset($attrs['name'])) {
5154
                                $this->currentOperation = $attrs['name'];
5155
                                $this->debug("current binding operation: $this->currentOperation");
5156
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
5157
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
5158
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
5159
                            }
5160
                            break;
5161
                        case 'input':
5162
                            $this->opStatus = 'input';
5163
                            break;
5164
                        case 'output':
5165
                            $this->opStatus = 'output';
5166
                            break;
5167
                        case 'body':
5168
                            if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5169
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5170
                            } else {
5171
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5172
                            }
5173
                            break;
5174
                    }
5175
                    break;
5176
                case 'service':
5177
                    switch ($name) {
5178
                        case 'port':
5179
                            $this->currentPort = $attrs['name'];
5180
                            $this->debug('current port: ' . $this->currentPort);
5181
                            $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5182
5183
                            break;
5184
                        case 'address':
5185
                            $this->ports[$this->currentPort]['location'] = $attrs['location'];
5186
                            $this->ports[$this->currentPort]['bindingType'] = $namespace;
5187
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
5188
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint'] = $attrs['location'];
5189
                            break;
5190
                    }
5191
                    break;
5192
            }
5193
            // set status
5194
            switch ($name) {
5195
                case 'import':
5196
                    if (isset($attrs['location'])) {
5197
                        $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
5198
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
5199
                    } else {
5200
                        $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
5201
                        if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
5202
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
5203
                        }
5204
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
5205
                    }
5206
                    break;
5207
                //wait for schema
5208
                //case 'types':
5209
                //  $this->status = 'schema';
5210
                //  break;
5211
                case 'message':
5212
                    $this->status = 'message';
5213
                    $this->messages[$attrs['name']] = array();
5214
                    $this->currentMessage = $attrs['name'];
5215
                    break;
5216
                case 'portType':
5217
                    $this->status = 'portType';
5218
                    $this->portTypes[$attrs['name']] = array();
5219
                    $this->currentPortType = $attrs['name'];
5220
                    break;
5221
                case "binding":
5222
                    if (isset($attrs['name'])) {
5223
                        // get binding name
5224
                        if (strpos($attrs['name'], ':')) {
5225
                            $this->currentBinding = $this->getLocalPart($attrs['name']);
5226
                        } else {
5227
                            $this->currentBinding = $attrs['name'];
5228
                        }
5229
                        $this->status = 'binding';
5230
                        $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5231
                        $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5232
                    }
5233
                    break;
5234
                case 'service':
5235
                    $this->serviceName = $attrs['name'];
5236
                    $this->status = 'service';
5237
                    $this->debug('current service: ' . $this->serviceName);
5238
                    break;
5239
                case 'definitions':
5240
                    foreach ($attrs as $name => $value) {
5241
                        $this->wsdl_info[$name] = $value;
5242
                    }
5243
                    break;
5244
            }
5245
        }
5246
    }
5247
5248
    /**
5249
     * end-element handler
5250
     *
5251
     * @param string $parser XML parser object
5252
     * @param string $name element name
5253
     * @access private
5254
     */
5255
    function end_element($parser, $name)
5256
    {
5257
        // unset schema status
5258
        if (
5259
/*preg_match('/types$/', $name) ||*/
5260
            preg_match('/schema$/', $name)
5261
        ) {
5262
            $this->status = "";
5263
            $this->appendDebug($this->currentSchema->getDebug());
5264
            $this->currentSchema->clearDebug();
5265
            $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5266
            $this->debug('Parsing WSDL schema done');
5267
        }
5268
        if ($this->status == 'schema') {
5269
            $this->currentSchema->schemaEndElement($parser, $name);
5270
        } else {
5271
            // bring depth down a notch
5272
            $this->depth--;
5273
        }
5274
        // end documentation
5275
        if ($this->documentation) {
5276
            //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5277
            //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5278
            $this->documentation = false;
5279
        }
5280
    }
5281
5282
    /**
5283
     * element content handler
5284
     *
5285
     * @param string $parser XML parser object
5286
     * @param string $data element content
5287
     * @access private
5288
     */
5289
    function character_data($parser, $data)
5290
    {
5291
        $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5292
        if (isset($this->message[$pos]['cdata'])) {
5293
            $this->message[$pos]['cdata'] .= $data;
5294
        }
5295
        if ($this->documentation) {
5296
            $this->documentation .= $data;
5297
        }
5298
    }
5299
5300
    /**
5301
     * if authenticating, set user credentials here
5302
     *
5303
     * @param    string $username
5304
     * @param    string $password
5305
     * @param    string $authtype (basic|digest|certificate|ntlm)
5306
     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5307
     * @access   public
5308
     */
5309
    function setCredentials($username, $password, $authtype = 'basic', $certRequest = array())
5310
    {
5311
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5312
        $this->appendDebug($this->varDump($certRequest));
5313
        $this->username = $username;
5314
        $this->password = $password;
5315
        $this->authtype = $authtype;
5316
        $this->certRequest = $certRequest;
5317
    }
5318
5319
    function getBindingData($binding)
5320
    {
5321
        if (is_array($this->bindings[$binding])) {
5322
            return $this->bindings[$binding];
5323
        }
5324
        return false;
5325
    }
5326
5327
    /**
5328
     * returns an assoc array of operation names => operation data
5329
     *
5330
     * @param string $portName WSDL port name
5331
     * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5332
     * @return array
5333
     * @access public
5334
     */
5335
    function getOperations($portName = '', $bindingType = 'soap')
5336
    {
5337
        $ops = array();
5338
        if ($bindingType == 'soap') {
5339
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5340
        } elseif ($bindingType == 'soap12') {
5341
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5342
        } else {
5343
            $this->debug("getOperations bindingType $bindingType may not be supported");
5344
        }
5345
        $this->debug("getOperations for port '$portName' bindingType $bindingType");
5346
        // loop thru ports
5347
        foreach ($this->ports as $port => $portData) {
5348
            $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5349
            if ($portName == '' || $port == $portName) {
5350
                // binding type of port matches parameter
5351
                if ($portData['bindingType'] == $bindingType) {
5352
                    $this->debug("getOperations found port $port bindingType $bindingType");
5353
                    //$this->debug("port data: " . $this->varDump($portData));
5354
                    //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5355
                    // merge bindings
5356
                    if (isset($this->bindings[$portData['binding']]['operations'])) {
5357
                        $ops = array_merge($ops, $this->bindings[$portData['binding']]['operations']);
5358
                    }
5359
                }
5360
            }
5361
        }
5362
        if (count($ops) == 0) {
5363
            $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5364
        }
5365
        return $ops;
5366
    }
5367
5368
    /**
5369
     * returns an associative array of data necessary for calling an operation
5370
     *
5371
     * @param string $operation name of operation
5372
     * @param string $bindingType type of binding eg: soap, soap12
5373
     * @return array
5374
     * @access public
5375
     */
5376
    function getOperationData($operation, $bindingType = 'soap')
5377
    {
5378
        if ($bindingType == 'soap') {
5379
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5380
        } elseif ($bindingType == 'soap12') {
5381
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5382
        }
5383
        // loop thru ports
5384
        foreach ($this->ports as $portData) {
5385
            // binding type of port matches parameter
5386
            if ($portData['bindingType'] == $bindingType) {
5387
                // get binding
5388
                //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5389
                // note that we could/should also check the namespace here
5390
                if (in_array($operation, array_keys($this->bindings[$portData['binding']]['operations'])))
5391
                {
5392
                    return $this->bindings[$portData['binding']]['operations'][$operation];
5393
                }
5394
            }
5395
        }
5396
        return array ();
5397
    }
5398
5399
    /**
5400
     * returns an associative array of data necessary for calling an operation
5401
     *
5402
     * @param string $soapAction soapAction for operation
5403
     * @param string $bindingType type of binding eg: soap, soap12
5404
     * @return array
5405
     * @access public
5406
     */
5407
    function getOperationDataForSoapAction($soapAction, $bindingType = 'soap')
5408
    {
5409
        if ($bindingType == 'soap') {
5410
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5411
        } elseif ($bindingType == 'soap12') {
5412
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5413
        }
5414
        // loop thru ports
5415
        foreach ($this->ports as $portData) {
5416
            // binding type of port matches parameter
5417
            if ($portData['bindingType'] == $bindingType) {
5418
                // loop through operations for the binding
5419
                foreach ($this->bindings[$portData['binding']]['operations'] as $opData) {
5420
                    if ($opData['soapAction'] == $soapAction) {
5421
                        return $opData;
5422
                    }
5423
                }
5424
            }
5425
        }
5426
        return array ();
5427
    }
5428
5429
    /**
5430
     * returns an array of information about a given type
5431
     * returns false if no type exists by the given name
5432
     *     typeDef = array(
5433
     *     'elements' => array(), // refs to elements array
5434
     *    'restrictionBase' => '',
5435
     *    'phpType' => '',
5436
     *    'order' => '(sequence|all)',
5437
     *    'attrs' => array() // refs to attributes array
5438
     *    )
5439
     *
5440
     * @param string $type the type
5441
     * @param string $ns namespace (not prefix) of the type
5442
     * @return false
5443
     * @access public
5444
     * @see nusoap_xmlschema
5445
     */
5446
    function getTypeDef($type, $ns)
5447
    {
5448
        $this->debug("in getTypeDef: type=$type, ns=$ns");
5449
        if ((!$ns) && isset($this->namespaces['tns'])) {
5450
            $ns = $this->namespaces['tns'];
5451
            $this->debug("in getTypeDef: type namespace forced to $ns");
5452
        }
5453
        if (!isset($this->schemas[$ns])) {
5454
            foreach ($this->schemas as $ns0 => $schema0) {
5455
                if (strcasecmp($ns, $ns0) == 0) {
5456
                    $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5457
                    $ns = $ns0;
5458
                    break;
5459
                }
5460
            }
5461
        }
5462
        if (isset($this->schemas[$ns])) {
5463
            $this->debug("in getTypeDef: have schema for namespace $ns");
5464
            for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
5465
                $xs = &$this->schemas[$ns][$i];
5466
                $t = $xs->getTypeDef($type);
5467
                $this->appendDebug($xs->getDebug());
5468
                $xs->clearDebug();
5469
                if ($t) {
5470
                    $this->debug("in getTypeDef: found type $type");
5471
                    if (!isset($t['phpType'])) {
5472
                        // get info for type to tack onto the element
5473
                        $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
5474
                        $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
5475
                        $etype = $this->getTypeDef($uqType, $ns);
5476
                        if ($etype) {
5477
                            $this->debug("found type for [element] $type:");
5478
                            $this->debug($this->varDump($etype));
5479
                            if (isset($etype['phpType'])) {
5480
                                $t['phpType'] = $etype['phpType'];
5481
                            }
5482
                            if (isset($etype['elements'])) {
5483
                                $t['elements'] = $etype['elements'];
5484
                            }
5485
                            if (isset($etype['attrs'])) {
5486
                                $t['attrs'] = $etype['attrs'];
5487
                            }
5488
                        } else {
5489
                            $this->debug("did not find type for [element] $type");
5490
                        }
5491
                    }
5492
                    return $t;
5493
                }
5494
            }
5495
            $this->debug("in getTypeDef: did not find type $type");
5496
        } else {
5497
            $this->debug("in getTypeDef: do not have schema for namespace $ns");
5498
        }
5499
        return false;
5500
    }
5501
5502
    /**
5503
     * prints html description of services
5504
     *
5505
     * @access private
5506
     */
5507
    function webDescription()
5508
    {
5509
        global $HTTP_SERVER_VARS;
5510
5511
        if (isset($_SERVER)) {
5512
            $PHP_SELF = $_SERVER['PHP_SELF'];
5513
        } elseif (isset($HTTP_SERVER_VARS)) {
5514
            $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5515
        } else {
5516
            $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
5517
            $PHP_SELF = '';
5518
        }
5519
5520
        $b = '
5521
		<html><head><title>NuSOAP: ' . $this->serviceName . '</title>
5522
		<style type="text/css">
5523
		    body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5524
		    p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5525
		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5526
		    ul      { margin-top: 10px; margin-left: 20px; }
5527
		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
5528
		    .content{
5529
			margin-left: 0px; padding-bottom: 2em; }
5530
		    .nav {
5531
			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5532
			margin-top: 10px; margin-left: 0px; color: #000000;
5533
			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5534
		    .title {
5535
			font-family: arial; font-size: 26px; color: #ffffff;
5536
			background-color: #999999; width: 100%;
5537
			margin-left: 0px; margin-right: 0px;
5538
			padding-top: 10px; padding-bottom: 10px;}
5539
		    .hidden {
5540
			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5541
			font-family: arial; overflow: hidden; width: 600;
5542
			padding: 20px; font-size: 10px; background-color: #999999;
5543
			layer-background-color:#FFFFFF; }
5544
		    a,a:active  { color: charcoal; font-weight: bold; }
5545
		    a:visited   { color: #666666; font-weight: bold; }
5546
		    a:hover     { color: cc3300; font-weight: bold; }
5547
		</style>
5548
		<script language="JavaScript" type="text/javascript">
5549
		<!--
5550
		// POP-UP CAPTIONS...
5551
		function lib_bwcheck(){ //Browsercheck (needed)
5552
		    this.ver=navigator.appVersion
5553
		    this.agent=navigator.userAgent
5554
		    this.dom=document.getElementById?1:0
5555
		    this.opera5=this.agent.indexOf("Opera 5")>-1
5556
		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5557
		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5558
		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5559
		    this.ie=this.ie4||this.ie5||this.ie6
5560
		    this.mac=this.agent.indexOf("Mac")>-1
5561
		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5562
		    this.ns4=(document.layers && !this.dom)?1:0;
5563
		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5564
		    return this
5565
		}
5566
		var bw = new lib_bwcheck()
5567
		//Makes crossbrowser object.
5568
		function makeObj(obj){
5569
		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5570
		    if(!this.evnt) return false
5571
		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5572
		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5573
		    this.writeIt=b_writeIt;
5574
		    return this
5575
		}
5576
		// A unit of measure that will be added when setting the position of a layer.
5577
		//var px = bw.ns4||window.opera?"":"px";
5578
		function b_writeIt(text){
5579
		    if (bw.ns4){this.wref.write(text);this.wref.close()}
5580
		    else this.wref.innerHTML = text
5581
		}
5582
		//Shows the messages
5583
		var oDesc;
5584
		function popup(divid){
5585
		    if(oDesc = new makeObj(divid)){
5586
			oDesc.css.visibility = "visible"
5587
		    }
5588
		}
5589
		function popout(){ // Hides message
5590
		    if(oDesc) oDesc.css.visibility = "hidden"
5591
		}
5592
		//-->
5593
		</script>
5594
		</head>
5595
		<body>
5596
		<div class=content>
5597
			<br><br>
5598
			<div class=title>' . $this->serviceName . '</div>
5599
			<div class=nav>
5600
				<p>View the <a href="?wsdl">WSDL</a> for the service.
5601
				Click on an operation name to view it&apos;s details.</p>
5602
				<ul>';
5603
        foreach ($this->getOperations() as $op => $data) {
5604
            $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5605
            // create hidden div
5606
            $b .= "<div id='$op' class='hidden'>
5607
				    <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
5608
            foreach ($data as $donnie => $marie) { // loop through opdata
5609
                if ($donnie == 'input' || $donnie == 'output') { // show input/output data
5610
                    $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
5611
                    foreach ($marie as $captain => $tenille) { // loop through data
5612
                        if ($captain == 'parts') { // loop thru parts
5613
                            $b .= "&nbsp;&nbsp;$captain:<br>";
5614
                            //if(is_array($tenille)){
5615
                            foreach ($tenille as $joanie => $chachi) {
5616
                                $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
5617
                            }
5618
                            //}
5619
                        } else {
5620
                            $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
5621
                        }
5622
                    }
5623
                } else {
5624
                    $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
5625
                }
5626
            }
5627
            $b .= '</div>';
5628
        }
5629
        $b .= '
5630
				<ul>
5631
			</div>
5632
		</div></body></html>';
5633
        return $b;
5634
    }
5635
5636
    /**
5637
     * serialize the parsed wsdl
5638
     *
5639
     * @param mixed $debug whether to put debug=1 in endpoint URL
5640
     * @return string serialization of WSDL
5641
     * @access public
5642
     */
5643
    function serialize($debug = 0)
5644
    {
5645
        $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5646
        $xml .= "\n<definitions";
5647
        foreach ($this->namespaces as $k => $v) {
5648
            $xml .= " xmlns:$k=\"$v\"";
5649
        }
5650
        // 10.9.02 - add poulter fix for wsdl and tns declarations
5651
        if (isset($this->namespaces['wsdl'])) {
5652
            $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
5653
        }
5654
        if (isset($this->namespaces['tns'])) {
5655
            $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
5656
        }
5657
        $xml .= '>';
5658
        // imports
5659
        if (sizeof($this->import) > 0) {
5660
            foreach ($this->import as $ns => $list) {
5661
                foreach ($list as $ii) {
5662
                    if ($ii['location'] != '') {
5663
                        $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
5664
                    } else {
5665
                        $xml .= '<import namespace="' . $ns . '" />';
5666
                    }
5667
                }
5668
            }
5669
        }
5670
        // types
5671
        if (count($this->schemas) >= 1) {
5672
            $xml .= "\n<types>\n";
5673
            foreach ($this->schemas as $list) {
5674
                foreach ($list as $xs) {
5675
                    $xml .= $xs->serializeSchema();
5676
                }
5677
            }
5678
            $xml .= '</types>';
5679
        }
5680
        // messages
5681
        if (count($this->messages) >= 1) {
5682
            foreach ($this->messages as $msgName => $msgParts) {
5683
                $xml .= "\n<message name=\"" . $msgName . '">';
5684
                if (is_array($msgParts)) {
5685
                    foreach ($msgParts as $partName => $partType) {
5686
                        // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5687
                        if (strpos($partType, ':')) {
5688
                            $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
5689
                        } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5690
                            // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5691
                            $typePrefix = 'xsd';
5692
                        } else {
5693
                            foreach ($this->typemap as $ns => $types) {
5694
                                if (isset($types[$partType])) {
5695
                                    $typePrefix = $this->getPrefixFromNamespace($ns);
5696
                                }
5697
                            }
5698
                            if (!isset($typePrefix)) {
5699
                                die("$partType has no namespace!");
5700
                            }
5701
                        }
5702
                        $ns = $this->getNamespaceFromPrefix($typePrefix);
5703
                        $localPart = $this->getLocalPart($partType);
5704
                        $typeDef = $this->getTypeDef($localPart, $ns);
5705
                        if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
5706
                            $elementortype = 'element';
5707
                            if (substr($localPart, -1) == '^') {
5708
                                $localPart = substr($localPart, 0, -1);
5709
                            }
5710
                        } else {
5711
                            $elementortype = 'type';
5712
                        }
5713
                        $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
5714
                    }
5715
                }
5716
                $xml .= '</message>';
5717
            }
5718
        }
5719
        // bindings & porttypes
5720
        if (count($this->bindings) >= 1) {
5721
            $binding_xml = '';
5722
            $portType_xml = '';
5723
            foreach ($this->bindings as $bindingName => $attrs) {
5724
                $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5725
                $binding_xml .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
5726
                $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5727
                foreach ($attrs['operations'] as $opName => $opParts) {
5728
                    $binding_xml .= "\n" . '  <operation name="' . $opName . '">';
5729
                    $binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $opParts['style'] . '"/>';
5730
                    if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
5731
                        $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5732
                    } else {
5733
                        $enc_style = '';
5734
                    }
5735
                    $binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
5736
                    if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
5737
                        $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5738
                    } else {
5739
                        $enc_style = '';
5740
                    }
5741
                    $binding_xml .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
5742
                    $binding_xml .= "\n" . '  </operation>';
5743
                    $portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
5744
                    if (isset($opParts['parameterOrder'])) {
5745
                        $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5746
                    }
5747
                    $portType_xml .= '>';
5748
                    if (isset($opParts['documentation']) && $opParts['documentation'] != '') {
5749
                        $portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5750
                    }
5751
                    $portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '"/>';
5752
                    $portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '"/>';
5753
                    $portType_xml .= "\n" . '  </operation>';
5754
                }
5755
                $portType_xml .= "\n" . '</portType>';
5756
                $binding_xml .= "\n" . '</binding>';
5757
            }
5758
            $xml .= $portType_xml . $binding_xml;
5759
        }
5760
        // services
5761
        $xml .= "\n<service name=\"" . $this->serviceName . '">';
5762
        if (count($this->ports) >= 1) {
5763
            foreach ($this->ports as $pName => $attrs) {
5764
                $xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5765
                $xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
5766
                $xml .= "\n" . '  </port>';
5767
            }
5768
        }
5769
        $xml .= "\n" . '</service>';
5770
        return $xml . "\n</definitions>";
5771
    }
5772
5773
    /**
5774
     * determine whether a set of parameters are unwrapped
5775
     * when they are expect to be wrapped, Microsoft-style.
5776
     *
5777
     * @param string $type the type (element name) of the wrapper
5778
     * @param array $parameters the parameter values for the SOAP call
5779
     * @return boolean whether they parameters are unwrapped (and should be wrapped)
5780
     * @access private
5781
     */
5782
    function parametersMatchWrapped($type, $parameters)
5783
    {
5784
        $this->debug("in parametersMatchWrapped type=$type, parameters=");
5785
        $this->appendDebug($this->varDump($parameters));
5786
5787
        // split type into namespace:unqualified-type
5788
        if (strpos($type, ':')) {
5789
            $uqType = substr($type, strrpos($type, ':') + 1);
5790
            $ns = substr($type, 0, strrpos($type, ':'));
5791
            $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5792
            if ($this->getNamespaceFromPrefix($ns)) {
5793
                $ns = $this->getNamespaceFromPrefix($ns);
5794
                $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5795
            }
5796
        } else {
5797
            // TODO: should the type be compared to types in XSD, and the namespace
5798
            // set to XSD if the type matches?
5799
            $this->debug("in parametersMatchWrapped: No namespace for type $type");
5800
            $ns = '';
5801
            $uqType = $type;
5802
        }
5803
5804
        // get the type information
5805
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5806
            $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5807
            return false;
5808
        }
5809
        $this->debug("in parametersMatchWrapped: found typeDef=");
5810
        $this->appendDebug($this->varDump($typeDef));
5811
        if (substr($uqType, -1) == '^') {
5812
            $uqType = substr($uqType, 0, -1);
5813
        }
5814
        $phpType = $typeDef['phpType'];
5815
        $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5816
        $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5817
5818
        // we expect a complexType or element of complexType
5819
        if ($phpType != 'struct') {
5820
            $this->debug("in parametersMatchWrapped: not a struct");
5821
            return false;
5822
        }
5823
5824
        // see whether the parameter names match the elements
5825
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5826
            $elements = 0;
5827
            $matches = 0;
5828
            foreach ($typeDef['elements'] as $name => $attrs) {
5829
                if (isset($parameters[$name])) {
5830
                    $this->debug("in parametersMatchWrapped: have parameter named $name");
5831
                    $matches++;
5832
                } else {
5833
                    $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5834
                }
5835
                $elements++;
5836
            }
5837
5838
            $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5839
            if ($matches == 0) {
5840
                return false;
5841
            }
5842
            return true;
5843
        }
5844
5845
        // since there are no elements for the type, if the user passed no
5846
        // parameters, the parameters match wrapped.
5847
        $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5848
        return count($parameters) == 0;
5849
    }
5850
5851
    /**
5852
     * serialize PHP values according to a WSDL message definition
5853
     * contrary to the method name, this is not limited to RPC
5854
     *
5855
     * TODO
5856
     * - multi-ref serialization
5857
     * - validate PHP values against type definitions, return errors if invalid
5858
     *
5859
     * @param string $operation operation name
5860
     * @param string $direction (input|output)
5861
     * @param mixed $parameters parameter value(s)
5862
     * @param string $bindingType (soap|soap12)
5863
     * @return false|string parameters serialized as XML or false on error (e.g. operation not found)
5864
     * @access public
5865
     */
5866
    function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap')
5867
    {
5868
        $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5869
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5870
5871
        if ($direction != 'input' && $direction != 'output') {
5872
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5873
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5874
            return false;
5875
        }
5876
        if (!$opData = $this->getOperationData($operation, $bindingType)) {
5877
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5878
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5879
            return false;
5880
        }
5881
        $this->debug('in serializeRPCParameters: opData:');
5882
        $this->appendDebug($this->varDump($opData));
5883
5884
        // Get encoding style for output and set to current
5885
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5886
        if (($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5887
            $encodingStyle = $opData['output']['encodingStyle'];
5888
        }
5889
5890
        // set input params
5891
        $xml = '';
5892
        if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5893
            $parts = &$opData[$direction]['parts'];
5894
            $part_count = sizeof($parts);
5895
            $style = $opData['style'];
5896
            $use = $opData[$direction]['use'];
5897
            $this->debug("have $part_count part(s) to serialize using $style/$use");
5898
            if (is_array($parameters)) {
5899
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5900
                $parameter_count = count($parameters);
5901
                $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5902
                // check for Microsoft-style wrapped parameters
5903
                if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
5904
                    $this->debug('check whether the caller has wrapped the parameters');
5905
                    if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
5906
                        // TODO: consider checking here for double-wrapping, when
5907
                        // service function wraps, then NuSOAP wraps again
5908
                        $this->debug("change simple array to associative with 'parameters' element");
5909
                        $parameters['parameters'] = $parameters[0];
5910
                        unset($parameters[0]);
5911
                    }
5912
                    if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
5913
                        $this->debug('check whether caller\'s parameters match the wrapped ones');
5914
                        if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5915
                            $this->debug('wrap the parameters for the caller');
5916
                            $parameters = array('parameters' => $parameters);
5917
                        }
5918
                    }
5919
                }
5920
                foreach ($parts as $name => $type) {
5921
                    $this->debug("serializing part $name of type $type");
5922
                    // Track encoding style
5923
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5924
                        $encodingStyle = $opData[$direction]['encodingStyle'];
5925
                        $enc_style = $encodingStyle;
5926
                    } else {
5927
                        $enc_style = false;
5928
                    }
5929
                    // NOTE: add error handling here
5930
                    // if serializeType returns false, then catch global error and fault
5931
                    if ($parametersArrayType == 'arraySimple') {
5932
                        $p = array_shift($parameters);
5933
                        $this->debug('calling serializeType w/indexed param');
5934
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5935
                    } elseif (isset($parameters[$name])) {
5936
                        $this->debug('calling serializeType w/named param');
5937
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5938
                    } else {
5939
                        // TODO: only send nillable
5940
                        $this->debug('calling serializeType w/null param');
5941
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5942
                    }
5943
                }
5944
            } else {
5945
                $this->debug('no parameters passed.');
5946
            }
5947
        }
5948
        $this->debug("serializeRPCParameters returning: $xml");
5949
        return $xml;
5950
    }
5951
5952
    /**
5953
     * serialize a PHP value according to a WSDL message definition
5954
     *
5955
     * TODO
5956
     * - multi-ref serialization
5957
     * - validate PHP values against type definitions, return errors if invalid
5958
     *
5959
     * @param string $operation operation name
5960
     * @param string $direction (input|output)
5961
     * @param mixed $parameters parameter value(s)
5962
     * @return false|string parameters serialized as XML or false on error (e.g. operation not found)
5963
     * @access public
5964
     * @deprecated
5965
     */
5966
    function serializeParameters($operation, $direction, $parameters)
5967
    {
5968
        $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5969
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5970
5971
        if ($direction != 'input' && $direction != 'output') {
5972
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5973
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5974
            return false;
5975
        }
5976
        if (!$opData = $this->getOperationData($operation)) {
5977
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5978
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5979
            return false;
5980
        }
5981
        $this->debug('opData:');
5982
        $this->appendDebug($this->varDump($opData));
5983
5984
        // Get encoding style for output and set to current
5985
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5986
        if (($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5987
            $encodingStyle = $opData['output']['encodingStyle'];
5988
        }
5989
5990
        // set input params
5991
        $xml = '';
5992
        if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5993
            $use = $opData[$direction]['use'];
5994
            $this->debug("use=$use");
5995
            $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5996
            if (is_array($parameters)) {
5997
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5998
                $this->debug('have ' . $parametersArrayType . ' parameters');
5999
                foreach ($opData[$direction]['parts'] as $name => $type) {
6000
                    $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
6001
                    // Track encoding style
6002
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
6003
                        $encodingStyle = $opData[$direction]['encodingStyle'];
6004
                        $enc_style = $encodingStyle;
6005
                    } else {
6006
                        $enc_style = false;
6007
                    }
6008
                    // NOTE: add error handling here
6009
                    // if serializeType returns false, then catch global error and fault
6010
                    if ($parametersArrayType == 'arraySimple') {
6011
                        $p = array_shift($parameters);
6012
                        $this->debug('calling serializeType w/indexed param');
6013
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
6014
                    } elseif (isset($parameters[$name])) {
6015
                        $this->debug('calling serializeType w/named param');
6016
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
6017
                    } else {
6018
                        // TODO: only send nillable
6019
                        $this->debug('calling serializeType w/null param');
6020
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
6021
                    }
6022
                }
6023
            } else {
6024
                $this->debug('no parameters passed.');
6025
            }
6026
        }
6027
        $this->debug("serializeParameters returning: $xml");
6028
        return $xml;
6029
    }
6030
6031
    /**
6032
     * serializes a PHP value according a given type definition
6033
     *
6034
     * @param string $name name of value (part or element)
6035
     * @param string $type XML schema type of value (type or element)
6036
     * @param mixed $value a native PHP value (parameter value)
6037
     * @param string $use use for part (encoded|literal)
6038
     * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6039
     * @param boolean $unqualified a kludge for what should be XML namespace form handling
6040
     * @return string value serialized as an XML string
6041
     * @access private
6042
     */
6043
    function serializeType($name, $type, $value, $use = 'encoded', $encodingStyle = false, $unqualified = false)
6044
    {
6045
        $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
6046
        $this->appendDebug("value=" . $this->varDump($value));
6047
        if ($use == 'encoded' && $encodingStyle) {
6048
            $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
6049
        }
6050
6051
        // if a soapval has been supplied, let its type override the WSDL
6052
        if (is_object($value) && get_class($value) == 'soapval') {
6053
            if ($value->type_ns) {
6054
                $type = $value->type_ns . ':' . $value->type;
6055
                $forceType = true;
6056
                $this->debug("in serializeType: soapval overrides type to $type");
6057
            } elseif ($value->type) {
6058
                $type = $value->type;
6059
                $forceType = true;
6060
                $this->debug("in serializeType: soapval overrides type to $type");
6061
            } else {
6062
                $forceType = false;
6063
                $this->debug("in serializeType: soapval does not override type");
6064
            }
6065
            $attrs = $value->attributes;
6066
            $value = $value->value;
6067
            $this->debug("in serializeType: soapval overrides value to $value");
6068
            if ($attrs) {
6069
                if (!is_array($value)) {
6070
                    $value['!'] = $value;
6071
                }
6072
                foreach ($attrs as $n => $v) {
6073
                    $value['!' . $n] = $v;
6074
                }
6075
                $this->debug("in serializeType: soapval provides attributes");
6076
            }
6077
        } else {
6078
            $forceType = false;
6079
        }
6080
6081
        $xml = '';
6082
        if (strpos($type, ':')) {
6083
            $uqType = substr($type, strrpos($type, ':') + 1);
6084
            $ns = substr($type, 0, strrpos($type, ':'));
6085
            $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
6086
            if ($this->getNamespaceFromPrefix($ns)) {
6087
                $ns = $this->getNamespaceFromPrefix($ns);
6088
                $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
6089
            }
6090
6091
            if ($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/') {
6092
                $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
6093
                if ($unqualified && $use == 'literal') {
6094
                    $elementNS = " xmlns=\"\"";
6095
                } else {
6096
                    $elementNS = '';
6097
                }
6098
                if (is_null($value)) {
6099
                    if ($use == 'literal') {
6100
                        // TODO: depends on minOccurs
6101
                        $xml = "<$name$elementNS/>";
6102
                    } else {
6103
                        // TODO: depends on nillable, which should be checked before calling this method
6104
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
6105
                    }
6106
                    $this->debug("in serializeType: returning: $xml");
6107
                    return $xml;
6108
                }
6109
                if ($uqType == 'Array') {
6110
                    // JBoss/Axis does this sometimes
6111
                    return $this->serialize_val($value, $name, false, false, false, false, $use);
6112
                }
6113
                if ($uqType == 'boolean') {
6114
                    if ((is_string($value) && $value == 'false') || (!$value)) {
6115
                        $value = 'false';
6116
                    } else {
6117
                        $value = 'true';
6118
                    }
6119
                }
6120
                if ($uqType == 'string' && gettype($value) == 'string') {
6121
                    $value = $this->expandEntities($value);
6122
                }
6123
                if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
6124
                    $value = sprintf("%.0lf", $value);
6125
                }
6126
                // it's a scalar
6127
                // TODO: what about null/nil values?
6128
                // check type isn't a custom type extending xmlschema namespace
6129
                if (!$this->getTypeDef($uqType, $ns)) {
6130
                    if ($use == 'literal') {
6131
                        if ($forceType) {
6132
                            $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6133
                        } else {
6134
                            $xml = "<$name$elementNS>$value</$name>";
6135
                        }
6136
                    } else {
6137
                        $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6138
                    }
6139
                    $this->debug("in serializeType: returning: $xml");
6140
                    return $xml;
6141
                }
6142
                $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
6143
            } elseif ($ns == 'http://xml.apache.org/xml-soap') {
6144
                $this->debug('in serializeType: appears to be Apache SOAP type');
6145
                if ($uqType == 'Map') {
6146
                    $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6147
                    if (!$tt_prefix) {
6148
                        $this->debug('in serializeType: Add namespace for Apache SOAP type');
6149
                        $tt_prefix = 'ns' . rand(1000, 9999);
6150
                        $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
6151
                        // force this to be added to usedNamespaces
6152
                        $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6153
                    }
6154
                    $contents = '';
6155
                    foreach ($value as $k => $v) {
6156
                        $this->debug("serializing map element: key $k, value $v");
6157
                        $contents .= '<item>';
6158
                        $contents .= $this->serialize_val($k, 'key', false, false, false, false, $use);
6159
                        $contents .= $this->serialize_val($v, 'value', false, false, false, false, $use);
6160
                        $contents .= '</item>';
6161
                    }
6162
                    if ($use == 'literal') {
6163
                        if ($forceType) {
6164
                            $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
6165
                        } else {
6166
                            $xml = "<$name>$contents</$name>";
6167
                        }
6168
                    } else {
6169
                        $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6170
                    }
6171
                    $this->debug("in serializeType: returning: $xml");
6172
                    return $xml;
6173
                }
6174
                $this->debug('in serializeType: Apache SOAP type, but only support Map');
6175
            }
6176
        } else {
6177
            // TODO: should the type be compared to types in XSD, and the namespace
6178
            // set to XSD if the type matches?
6179
            $this->debug("in serializeType: No namespace for type $type");
6180
            $ns = '';
6181
            $uqType = $type;
6182
        }
6183
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
6184
            $this->setError("$type ($uqType) is not a supported type.");
6185
            $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6186
            return false;
6187
        } else {
6188
            $this->debug("in serializeType: found typeDef");
6189
            $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6190
            if (substr($uqType, -1) == '^') {
6191
                $uqType = substr($uqType, 0, -1);
6192
            }
6193
        }
6194
        if (!isset($typeDef['phpType'])) {
6195
            $this->setError("$type ($uqType) has no phpType.");
6196
            $this->debug("in serializeType: $type ($uqType) has no phpType.");
6197
            return false;
6198
        }
6199
        $phpType = $typeDef['phpType'];
6200
        $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
6201
        // if php type == struct, map value to the <all> element names
6202
        if ($phpType == 'struct') {
6203
            if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
6204
                $elementName = $uqType;
6205
                if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6206
                    $elementNS = " xmlns=\"$ns\"";
6207
                } else {
6208
                    $elementNS = " xmlns=\"\"";
6209
                }
6210
            } else {
6211
                $elementName = $name;
6212
                if ($unqualified) {
6213
                    $elementNS = " xmlns=\"\"";
6214
                } else {
6215
                    $elementNS = '';
6216
                }
6217
            }
6218
            if (is_null($value)) {
6219
                if ($use == 'literal') {
6220
                    // TODO: depends on minOccurs and nillable
6221
                    $xml = "<$elementName$elementNS/>";
6222
                } else {
6223
                    $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
6224
                }
6225
                $this->debug("in serializeType: returning: $xml");
6226
                return $xml;
6227
            }
6228
            if (is_object($value)) {
6229
                $value = get_object_vars($value);
6230
            }
6231
            if (is_array($value)) {
6232
                $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6233
                if ($use == 'literal') {
6234
                    if ($forceType) {
6235
                        $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6236
                    } else {
6237
                        $xml = "<$elementName$elementNS$elementAttrs>";
6238
                    }
6239
                } else {
6240
                    $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6241
                }
6242
6243
                if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
6244
                    if (isset($value['!'])) {
6245
                        $xml .= $value['!'];
6246
                        $this->debug("in serializeType: serialized simpleContent for type $type");
6247
                    } else {
6248
                        $this->debug("in serializeType: no simpleContent to serialize for type $type");
6249
                    }
6250
                } else {
6251
                    // complexContent
6252
                    $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6253
                }
6254
                $xml .= "</$elementName>";
6255
            } else {
6256
                $this->debug("in serializeType: phpType is struct, but value is not an array");
6257
                $this->setError("phpType is struct, but value is not an array: see debug output for details");
6258
            }
6259
        } elseif ($phpType == 'array') {
6260
            if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6261
                $elementNS = " xmlns=\"$ns\"";
6262
            } else {
6263
                if ($unqualified) {
6264
                    $elementNS = " xmlns=\"\"";
6265
                } else {
6266
                    $elementNS = '';
6267
                }
6268
            }
6269
            if (is_null($value)) {
6270
                if ($use == 'literal') {
6271
                    // TODO: depends on minOccurs
6272
                    $xml = "<$name$elementNS/>";
6273
                } else {
6274
                    $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
6275
                        $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6276
                        ":Array\" " .
6277
                        $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6278
                        ':arrayType="' .
6279
                        $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
6280
                        ':' .
6281
                        $this->getLocalPart($typeDef['arrayType']) . "[0]\"/>";
6282
                }
6283
                $this->debug("in serializeType: returning: $xml");
6284
                return $xml;
6285
            }
6286
            $cols = '';
6287
            if (isset($typeDef['multidimensional'])) {
6288
                $nv = array();
6289
                foreach ($value as $v) {
6290
                    $cols = ',' . sizeof($v);
6291
                    $nv = array_merge($nv, $v);
6292
                }
6293
                $value = $nv;
6294
            }
6295
            if (is_array($value) && sizeof($value) >= 1) {
6296
                $rows = sizeof($value);
6297
                $contents = '';
6298
                foreach ($value as $v) {
6299
                    //$this->debug breaks when serializing ArrayOfComplexType
6300
                    //Error: Object of class [COMPLEX-TYPE] could not be converted to string
6301
                    //$this->debug("serializing array element: $k, " . (is_array($v) ? "array" : $v) . " of type: $typeDef[arrayType]");
6302
                    //if (strpos($typeDef['arrayType'], ':') ) {
6303
                    if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6304
                        $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6305
                    } else {
6306
                        $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6307
                    }
6308
                }
6309
            } else {
6310
                $rows = 0;
6311
                $contents = null;
6312
            }
6313
            // TODO: for now, an empty value will be serialized as a zero element
6314
            // array.  Revisit this when coding the handling of null/nil values.
6315
            if ($use == 'literal') {
6316
                $xml = "<$name$elementNS>"
6317
                    . $contents
6318
                    . "</$name>";
6319
            } else {
6320
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' .
6321
                    $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6322
                    . ':arrayType="'
6323
                    . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6324
                    . ":" . $this->getLocalPart($typeDef['arrayType']) . "[$rows$cols]\">"
6325
                    . $contents
6326
                    . "</$name>";
6327
            }
6328
        } elseif ($phpType == 'scalar') {
6329
            if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6330
                $elementNS = " xmlns=\"$ns\"";
6331
            } else {
6332
                if ($unqualified) {
6333
                    $elementNS = " xmlns=\"\"";
6334
                } else {
6335
                    $elementNS = '';
6336
                }
6337
            }
6338
            if ($use == 'literal') {
6339
                if ($forceType) {
6340
                    $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6341
                } else {
6342
                    $xml = "<$name$elementNS>$value</$name>";
6343
                }
6344
            } else {
6345
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6346
            }
6347
        }
6348
        $this->debug("in serializeType: returning: $xml");
6349
        return $xml;
6350
    }
6351
6352
    /**
6353
     * serializes the attributes for a complexType
6354
     *
6355
     * @param array $typeDef our internal representation of an XML schema type (or element)
6356
     * @param mixed $value a native PHP value (parameter value)
6357
     * @param string $ns the namespace of the type
6358
     * @param string $uqType the local part of the type
6359
     * @return string value serialized as an XML string
6360
     * @access private
6361
     */
6362
    function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType)
6363
    {
6364
        $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6365
        $xml = '';
6366
        if (isset($typeDef['extensionBase'])) {
6367
            $nsx = $this->getPrefix($typeDef['extensionBase']);
6368
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6369
            if ($this->getNamespaceFromPrefix($nsx)) {
6370
                $nsx = $this->getNamespaceFromPrefix($nsx);
6371
            }
6372
            if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6373
                $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6374
                $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6375
            } else {
6376
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6377
            }
6378
        }
6379
        if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6380
            $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6381
            if (is_array($value)) {
6382
                $xvalue = $value;
6383
            } elseif (is_object($value)) {
6384
                $xvalue = get_object_vars($value);
6385
            } else {
6386
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6387
                $xvalue = array();
6388
            }
6389
            foreach ($typeDef['attrs'] as $aName => $attrs) {
6390
                if (isset($xvalue['!' . $aName])) {
6391
                    $xname = '!' . $aName;
6392
                    $this->debug("value provided for attribute $aName with key $xname");
6393
                } elseif (isset($xvalue[$aName])) {
6394
                    $xname = $aName;
6395
                    $this->debug("value provided for attribute $aName with key $xname");
6396
                } elseif (isset($attrs['default'])) {
6397
                    $xname = '!' . $aName;
6398
                    $xvalue[$xname] = $attrs['default'];
6399
                    $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6400
                } else {
6401
                    $xname = '';
6402
                    $this->debug("no value provided for attribute $aName");
6403
                }
6404
                if ($xname) {
6405
                    $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
6406
                }
6407
            }
6408
        } else {
6409
            $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6410
        }
6411
        return $xml;
6412
    }
6413
6414
    /**
6415
     * serializes the elements for a complexType
6416
     *
6417
     * @param array $typeDef our internal representation of an XML schema type (or element)
6418
     * @param mixed $value a native PHP value (parameter value)
6419
     * @param string $ns the namespace of the type
6420
     * @param string $uqType the local part of the type
6421
     * @param string $use use for part (encoded|literal)
6422
     * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6423
     * @return string value serialized as an XML string
6424
     * @access private
6425
     */
6426
    function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use = 'encoded', $encodingStyle = false)
6427
    {
6428
        $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6429
        $xml = '';
6430
        if (isset($typeDef['extensionBase'])) {
6431
            $nsx = $this->getPrefix($typeDef['extensionBase']);
6432
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6433
            if ($this->getNamespaceFromPrefix($nsx)) {
6434
                $nsx = $this->getNamespaceFromPrefix($nsx);
6435
            }
6436
            if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6437
                $this->debug("serialize elements for extension base $nsx:$uqTypex");
6438
                $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
6439
            } else {
6440
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6441
            }
6442
        }
6443
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6444
            $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6445
            if (is_array($value)) {
6446
                $xvalue = $value;
6447
            } elseif (is_object($value)) {
6448
                $xvalue = get_object_vars($value);
6449
            } else {
6450
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6451
                $xvalue = array();
6452
            }
6453
            // toggle whether all elements are present - ideally should validate against schema
6454
            if (count($typeDef['elements']) != count($xvalue)) {
6455
                $optionals = true;
6456
            }
6457
            foreach ($typeDef['elements'] as $eName => $attrs) {
6458
                if (!isset($xvalue[$eName])) {
6459
                    if (isset($attrs['default'])) {
6460
                        $xvalue[$eName] = $attrs['default'];
6461
                        $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6462
                    }
6463
                }
6464
                // if user took advantage of a minOccurs=0, then only serialize named parameters
6465
                if (
6466
                    isset($optionals)
6467
                    && (!isset($xvalue[$eName]))
6468
                    && ((!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
6469
                ) {
6470
                    if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
6471
                        $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6472
                    }
6473
                    // do nothing
6474
                    $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6475
                } else {
6476
                    // get value
6477
                    if (isset($xvalue[$eName])) {
6478
                        $v = $xvalue[$eName];
6479
                    } else {
6480
                        $v = null;
6481
                    }
6482
                    if (isset($attrs['form'])) {
6483
                        $unqualified = ($attrs['form'] == 'unqualified');
6484
                    } else {
6485
                        $unqualified = false;
6486
                    }
6487
                    if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
6488
                        $vv = $v;
6489
                        foreach ($vv as $v) {
6490
                            if (isset($attrs['type']) || isset($attrs['ref'])) {
6491
                                // serialize schema-defined type
6492
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6493
                            } else {
6494
                                // serialize generic type (can this ever really happen?)
6495
                                $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6496
                                $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6497
                            }
6498
                        }
6499
                    } else {
6500
                        if (!is_null($v) || !isset($attrs['minOccurs']) || $attrs['minOccurs'] != '0')
6501
                        {
6502
                            if (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true')
6503
                            {
6504
                                // TODO: serialize a nil correctly, but for now serialize schema-defined type
6505
                                $xml .= $this->serializeType(
6506
                                    $eName,
6507
                                                              isset($attrs['type']) ? $attrs['type'] : $attrs['ref'],
6508
                                                              $v, $use, $encodingStyle, $unqualified);
6509
                            }
6510
                            elseif (isset($attrs['type']) || isset($attrs['ref']))
6511
                            {
6512
                                // serialize schema-defined type
6513
                                $xml .= $this->serializeType(
6514
                                    $eName,
6515
                                                              isset($attrs['type']) ? $attrs['type'] : $attrs['ref'],
6516
                                                              $v, $use, $encodingStyle, $unqualified);
6517
                            }
6518
                            else
6519
                            {
6520
                                // serialize generic type (can this ever really happen?)
6521
                                $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6522
                                $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6523
                            }
6524
                        }
6525
                    }
6526
                }
6527
            }
6528
        } else {
6529
            $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6530
        }
6531
        return $xml;
6532
    }
6533
6534
    /**
6535
     * adds an XML Schema complex type to the WSDL types
6536
     *
6537
     * @param string $name
6538
     * @param string $typeClass (complexType|simpleType|attribute)
6539
     * @param string $phpType currently supported are array and struct (php assoc array)
6540
     * @param string $compositor (all|sequence|choice)
6541
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6542
     * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
6543
     * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6544
     * @param string $arrayType as namespace:name (xsd:string)
6545
     * @see nusoap_xmlschema
6546
     * @access public
6547
     */
6548
    function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = array(), $attrs = array(), $arrayType = '')
6549
    {
6550
        if (count($elements) > 0) {
6551
            $eElements = array();
6552
            foreach ($elements as $n => $e) {
6553
                // expand each element
6554
                $ee = array();
6555
                foreach ($e as $k => $v) {
6556
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
6557
                    $v = strpos($v, ':') ? $this->expandQname($v) : $v;
6558
                    $ee[$k] = $v;
6559
                }
6560
                $eElements[$n] = $ee;
6561
            }
6562
            $elements = $eElements;
6563
        }
6564
6565
        if (is_array($attrs) && count($attrs) > 0) {
6566
            $eAttrs = array ();
6567
            foreach ($attrs as $n => $a) {
6568
                $aa = array ();
6569
                // expand each attribute
6570
                foreach ($a as $k => $v) {
6571
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
6572
                    $v = strpos($v, ':') ? $this->expandQname($v) : $v;
6573
                    $aa[$k] = $v;
6574
                }
6575
                $eAttrs[$n] = $aa;
6576
            }
6577
            $attrs = $eAttrs;
6578
        }
6579
6580
        $restrictionBase = strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6581
        $arrayType = strpos($arrayType, ':') ? $this->expandQname($arrayType) : $arrayType;
6582
6583
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6584
        $this->schemas[$typens][0]->addComplexType($name, $typeClass, $phpType, $compositor, $restrictionBase, $elements, $attrs, $arrayType);
6585
    }
6586
6587
    /**
6588
     * adds an XML Schema simple type to the WSDL types
6589
     *
6590
     * @param string $name
6591
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6592
     * @param string $typeClass (should always be simpleType)
6593
     * @param string $phpType (should always be scalar)
6594
     * @param array $enumeration array of values
6595
     * @see nusoap_xmlschema
6596
     * @access public
6597
     */
6598
    function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = array())
6599
    {
6600
        $restrictionBase = strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6601
6602
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6603
        $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6604
    }
6605
6606
    /**
6607
     * adds an element to the WSDL types
6608
     *
6609
     * @param array $attrs attributes that must include name and type
6610
     * @see nusoap_xmlschema
6611
     * @access public
6612
     */
6613
    function addElement($attrs)
6614
    {
6615
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6616
        $this->schemas[$typens][0]->addElement($attrs);
6617
    }
6618
6619
    /**
6620
     * register an operation with the server
6621
     *
6622
     * @param string $name operation (method) name
6623
     * @param array $in assoc array of input values: key = param name, value = param type
6624
     * @param array $out assoc array of output values: key = param name, value = param type
6625
     * @param string $namespace optional The namespace for the operation
6626
     * @param string $soapaction optional The soapaction for the operation
6627
     * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
6628
     * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
6629
     * @param string $documentation optional The description to include in the WSDL
6630
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6631
     * @param string $customResponseTagName optional Name of the outgoing response
6632
     * @access public
6633
     */
6634
    function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = '', $customResponseTagName = '')
6635
    {
6636
        if ($use == 'encoded' && $encodingStyle == '') {
6637
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6638
        }
6639
6640
        if ($style == 'document') {
6641
            $elements = array();
6642
            foreach ($in as $n => $t) {
6643
                $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6644
            }
6645
            $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6646
            $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
6647
            $in = array('parameters' => 'tns:' . $name . '^');
6648
6649
            $elements = array();
6650
            foreach ($out as $n => $t) {
6651
                $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6652
            }
6653
            $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6654
            $this->addElement(array('name' => $customResponseTagName, 'type' => $name . 'ResponseType', 'form' => 'qualified'));
6655
            $out = array('parameters' => 'tns:' . $customResponseTagName . '^');
6656
        }
6657
6658
        // get binding
6659
        $this->bindings[$this->serviceName . 'Binding']['operations'][$name] =
6660
            array(
6661
                'name' => $name,
6662
                'binding' => $this->serviceName . 'Binding',
6663
                'endpoint' => $this->endpoint,
6664
                'soapAction' => $soapaction,
6665
                'style' => $style,
6666
                'input' => array(
6667
                    'use' => $use,
6668
                    'namespace' => $namespace,
6669
                    'encodingStyle' => $encodingStyle,
6670
                    'message' => $name,
6671
                    'parts' => $in),
6672
                'output' => array(
6673
                    'use' => $use,
6674
                    'namespace' => $namespace,
6675
                    'encodingStyle' => $encodingStyle,
6676
                    'message' => $customResponseTagName,
6677
                    'parts' => $out),
6678
                'namespace' => $namespace,
6679
                'transport' => 'http://schemas.xmlsoap.org/soap/http',
6680
                'documentation' => $documentation);
6681
        // add portTypes
6682
        // add messages
6683
        if ($in) {
6684
            foreach ($in as $pName => $pType) {
6685
                if (strpos($pType, ':')) {
6686
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
6687
                }
6688
                $this->messages[$name][$pName] = $pType;
6689
            }
6690
        } else {
6691
            $this->messages[$name] = '0';
6692
        }
6693
        if ($out) {
6694
            foreach ($out as $pName => $pType) {
6695
                if (strpos($pType, ':')) {
6696
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ":" . $this->getLocalPart($pType);
6697
                }
6698
                $this->messages[$customResponseTagName][$pName] = $pType;
6699
            }
6700
        } else {
6701
            $this->messages[$customResponseTagName] = '0';
6702
        }
6703
        return true;
6704
    }
6705
}
6706
6707
6708
/**
6709
 *
6710
 * nusoap_parser class parses SOAP XML messages into native PHP values
6711
 *
6712
 * @author   Dietrich Ayala <[email protected]>
6713
 * @author   Scott Nichol <[email protected]>
6714
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
6715
 * @access   public
6716
 */
6717
class nusoap_parser extends nusoap_base
6718
{
6719
    var $parser = null;
6720
    var $methodNamespace = '';
6721
    var $xml = '';
6722
    var $xml_encoding = '';
6723
    var $method = '';
6724
    var $root_struct = '';
6725
    var $root_struct_name = '';
6726
    var $root_struct_namespace = '';
6727
    var $root_header = '';
6728
    var $document = '';            // incoming SOAP body (text)
6729
    // determines where in the message we are (envelope,header,body,method)
6730
    var $status = '';
6731
    var $position = 0;
6732
    var $depth = 0;
6733
    var $default_namespace = '';
6734
    var $namespaces = array();
6735
    var $message = array();
6736
    var $parent = '';
6737
    var $fault = false;
6738
    var $fault_code = '';
6739
    var $fault_str = '';
6740
    var $fault_detail = '';
6741
    var $depth_array = array();
6742
    var $debug_flag = true;
6743
    var $soapresponse = null;    // parsed SOAP Body
6744
    var $soapheader = null;        // parsed SOAP Header
6745
    var $responseHeaders = '';    // incoming SOAP headers (text)
6746
    var $body_position = 0;
6747
    // for multiref parsing:
6748
    // array of id => pos
6749
    var $ids = array();
6750
    // array of id => hrefs => pos
6751
    var $multirefs = array();
6752
    // toggle for auto-decoding element content
6753
    var $decode_utf8 = true;
6754
6755
    var $attachments = array();
6756
6757
    /**
6758
     * constructor that actually does the parsing
6759
     *
6760
     * @param    string $xml SOAP message
6761
     * @param    string $encoding character encoding scheme of message
6762
     * @param    string $method method for which XML is parsed (unused?)
6763
     * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6764
     * @access   public
6765
     */
6766
    function __construct($xml, $encoding = 'UTF-8', $method = '', $decode_utf8 = true)
6767
    {
6768
        parent::__construct();
6769
        $this->xml = $xml;
6770
        $this->xml_encoding = $encoding;
6771
        $this->method = $method;
6772
        $this->decode_utf8 = $decode_utf8;
6773
        $this->attachments = array();
6774
6775
        // Check whether content has been read.
6776
        if (!empty($xml)) {
6777
            // Check XML encoding
6778
            $pos_xml = strpos($xml, '<?xml');
6779
            if ($pos_xml !== false) {
6780
                $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6781
                if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6782
                    $xml_encoding = $res[1];
6783
                    if (strtoupper($xml_encoding) != $encoding) {
6784
                        $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6785
                        $this->debug($err);
6786
                        if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
6787
                            $this->setError($err);
6788
                            return;
6789
                        }
6790
                        // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6791
                    } else {
6792
                        $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6793
                    }
6794
                } else {
6795
                    $this->debug('No encoding specified in XML declaration');
6796
                }
6797
            } else {
6798
                $this->debug('No XML declaration');
6799
            }
6800
            $this->debug('Entering nusoap_parser(), length=' . strlen($xml) . ', encoding=' . $encoding);
6801
            // Create an XML parser - why not xml_parser_create_ns?
6802
            $this->parser = xml_parser_create($this->xml_encoding);
6803
            // Set the options for parsing the XML data.
6804
            //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6805
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6806
            xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6807
            // Set the object for the parser.
6808
            xml_set_object($this->parser, $this);
6809
            // Set the element handlers for the parser.
6810
            xml_set_element_handler($this->parser, 'start_element', 'end_element');
6811
            xml_set_character_data_handler($this->parser, 'character_data');
6812
            $parseErrors = array();
6813
            $chunkSize = 4096;
6814
            for($pointer = 0; $pointer < strlen($xml) && empty($parseErrors); $pointer += $chunkSize) {
6815
                $xmlString = substr($xml, $pointer, $chunkSize);
6816
                if(!xml_parse($this->parser, $xmlString)) {
6817
                    $parseErrors['lineNumber'] = xml_get_current_line_number($this->parser);
6818
                    $parseErrors['errorString'] = xml_error_string(xml_get_error_code($this->parser));
6819
                }
6820
            }
6821
            //Tell the script that is the end of the parsing (by setting is_final to TRUE)
6822
            xml_parse($this->parser, '', true);
6823
6824
            // Check if there is any attachment
6825
            $this->attachments = array();
6826
            foreach(preg_split("/((\r?\n)|(\r\n?))/", $xml) as $line){
6827
                if(preg_match(("/^--(.*)/"), $line, $matches)) {
6828
                    $this->attachments[] = array ();
6829
                    $this->attachments[count($this->attachments) - 1]['boundaryStr'] = $matches[1];
6830
                } elseif(preg_match(("/Content-Type:(.*)/"), $line, $matches)) {
6831
                    $this->attachments[count($this->attachments) - 1]['Content-Type'] = $matches[1];
6832
                } elseif(preg_match(("/Content-Id:(.*)/"), $line, $matches)) {
6833
                    $this->attachments[count($this->attachments) - 1]['Content-Id'] = $matches[1];
6834
                } elseif(preg_match(("/Content-Transfer-Encoding:(.*)/"), $line, $matches)) {
6835
                    $this->attachments[count($this->attachments) - 1]['Content-Transfer-Encoding'] = $matches[1];
6836
                }
6837
            }
6838
6839
            if(!empty($this->attachments)) {
6840
                // Extract the content of each attachments
6841
                $substrXml = $xml;
6842
                foreach($this->attachments as $key => $attachment) {
6843
                    $startPos = max(
6844
                        stripos($substrXml, $attachment['boundaryStr']),
6845
                        (array_key_exists('Content-Type', $attachment) ? stripos($substrXml, $attachment['Content-Type']) : 0),
6846
                        (array_key_exists('Content-Id', $attachment) ? stripos($substrXml, $attachment['Content-Id']) : 0),
6847
                        (array_key_exists('Content-Transfer-Encoding', $attachment) ? stripos($substrXml, $attachment['Content-Transfer-Encoding']) : 0)
6848
                    );
6849
                    $substrXml = substr($substrXml, $startPos);
6850
                    $startPos = stripos($substrXml, PHP_EOL);
6851
                    $substrXml = substr($substrXml, $startPos);
6852
                    $substrXml = trim($substrXml);
6853
                    $length = null;
6854
                    if(array_key_exists($key + 1, $this->attachments) && $this->attachments[$key + 1] && !empty($this->attachments[$key + 1]['boundaryStr'])) {
6855
                        $length = stripos($substrXml, ('--' . $this->attachments[$key + 1]['boundaryStr'])) - 1;
6856
                    }
6857
                    $content = substr($substrXml, 0, $length);
6858
                    $this->attachments[$key]['content'] = $content;
6859
                }
6860
            }
6861
6862
            if(!empty($parseErrors) && !empty($this->attachments)){
6863
                // Search the SOAP response message
6864
                foreach($this->attachments as $key => $attachment) {
6865
                    // Settings for xml_parse
6866
                    $this->parser = xml_parser_create($this->xml_encoding);
6867
                    xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6868
                    xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6869
                    xml_set_object($this->parser, $this);
6870
                    xml_set_element_handler($this->parser, 'start_element', 'end_element');
6871
                    xml_set_character_data_handler($this->parser, 'character_data');
6872
6873
                    if(!empty($attachment['content'])) {
6874
                        $content = $attachment['content'];
6875
                        foreach(preg_split("/((\r?\n)|(\r\n?))/", $content) as $line){
6876
                            if(preg_match(("/:Envelope/"), $line, $matches)) {
6877
                                if(!xml_parse($this->parser, $content, true)) {
6878
                                    $parseErrors['lineNumber'] = xml_get_current_line_number($this->parser);
6879
                                    $parseErrors['errorString'] = xml_error_string(xml_get_error_code($this->parser));
6880
                                } else {
6881
                                    $parseErrors = array();
6882
                                    unset($this->attachments[$key]);
6883
                                    break 2;
6884
                                }
6885
                            }
6886
                        }
6887
                    }
6888
                }
6889
            }
6890
6891
            if(!empty($parseErrors)){
6892
                // Display an error message.
6893
                $err = sprintf(
6894
                    'XML error parsing SOAP payload on line %d: %s',
6895
                        $parseErrors['lineNumber'],
6896
                        $parseErrors['errorString']);
6897
                $this->debug($err);
6898
                $this->setError($err);
6899
            } else {
6900
                $this->debug('in nusoap_parser ctor, message:');
6901
                $this->appendDebug($this->varDump($this->message));
6902
                $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
6903
                // get final value
6904
                $this->soapresponse = $this->message[$this->root_struct]['result'];
6905
                // get header value
6906
                if ($this->root_header != '' && isset($this->message[$this->root_header]['result'])) {
6907
                    $this->soapheader = $this->message[$this->root_header]['result'];
6908
                }
6909
                // resolve hrefs/ids
6910
                if (sizeof($this->multirefs) > 0) {
6911
                    foreach ($this->multirefs as $id => $hrefs) {
6912
                        $this->debug('resolving multirefs for id: ' . $id);
6913
                        $idVal = $this->buildVal($this->ids[$id]);
6914
                        if (is_array($idVal) && isset($idVal['!id'])) {
6915
                            unset($idVal['!id']);
6916
                        }
6917
                        foreach ($hrefs as $refPos => $ref) {
6918
                            $this->debug('resolving href at pos ' . $refPos);
6919
                            $this->multirefs[$id][$refPos] = $idVal;
6920
                        }
6921
                    }
6922
                }
6923
            }
6924
            xml_parser_free($this->parser);
6925
            unset($this->parser);
6926
        } else {
6927
            $this->debug('xml was empty, didn\'t parse!');
6928
            $this->setError('xml was empty, didn\'t parse!');
6929
        }
6930
    }
6931
6932
    /**
6933
     * start-element handler
6934
     *
6935
     * @param    resource $parser XML parser object
6936
     * @param    string $name element name
6937
     * @param    array $attrs associative array of attributes
6938
     * @access   private
6939
     */
6940
    function start_element($parser, $name, $attrs)
6941
    {
6942
        // position in a total number of elements, starting from 0
6943
        // update class level pos
6944
        $pos = $this->position++;
6945
        // and set mine
6946
        $this->message[$pos] = array('pos' => $pos, 'children' => '', 'cdata' => '');
6947
        // depth = how many levels removed from root?
6948
        // set mine as current global depth and increment global depth value
6949
        $this->message[$pos]['depth'] = $this->depth++;
6950
6951
        // else add self as child to whoever the current parent is
6952
        if ($pos != 0) {
6953
            $this->message[$this->parent]['children'] .= '|' . $pos;
6954
        }
6955
        // set my parent
6956
        $this->message[$pos]['parent'] = $this->parent;
6957
        // set self as current parent
6958
        $this->parent = $pos;
6959
        // set self as current value for this depth
6960
        $this->depth_array[$this->depth] = $pos;
6961
        // get element prefix
6962
        if (strpos($name, ':')) {
6963
            // get ns prefix
6964
            $prefix = substr($name, 0, strpos($name, ':'));
6965
            // get unqualified name
6966
            $name = substr(strstr($name, ':'), 1);
6967
        }
6968
        // set status
6969
        if ($name == 'Envelope' && $this->status == '') {
6970
            $this->status = 'envelope';
6971
        } elseif ($name == 'Header' && $this->status == 'envelope') {
6972
            $this->root_header = $pos;
6973
            $this->status = 'header';
6974
        } elseif ($name == 'Body' && $this->status == 'envelope') {
6975
            $this->status = 'body';
6976
            $this->body_position = $pos;
6977
            // set method
6978
        } elseif ($this->status == 'body' && $pos == ($this->body_position + 1)) {
6979
            $this->status = 'method';
6980
            $this->root_struct_name = $name;
6981
            $this->root_struct = $pos;
6982
            $this->message[$pos]['type'] = 'struct';
6983
            $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6984
        }
6985
        // set my status
6986
        $this->message[$pos]['status'] = $this->status;
6987
        // set name
6988
        $this->message[$pos]['name'] = htmlspecialchars($name);
6989
        // set attrs
6990
        $this->message[$pos]['attrs'] = $attrs;
6991
6992
        // loop through atts, logging ns and type declarations
6993
        $attstr = '';
6994
        foreach ($attrs as $key => $value) {
6995
            $key_prefix = $this->getPrefix($key);
6996
            $key_localpart = $this->getLocalPart($key);
6997
            // if ns declarations, add to class level array of valid namespaces
6998
            if ($key_prefix == 'xmlns') {
6999
                if (preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) {
7000
                    $this->XMLSchemaVersion = $value;
7001
                    $this->namespaces['xsd'] = $this->XMLSchemaVersion;
7002
                    $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
7003
                }
7004
                $this->namespaces[$key_localpart] = $value;
7005
                // set method namespace
7006
                if ($name == $this->root_struct_name) {
7007
                    $this->methodNamespace = $value;
7008
                }
7009
                // if it's a type declaration, set type
7010
            } elseif ($key_localpart == 'type') {
7011
                if (!isset($this->message[$pos]['type']) || $this->message[$pos]['type'] != 'array')
7012
                {
7013
                    $value_prefix = $this->getPrefix($value);
7014
                    $value_localpart = $this->getLocalPart($value);
7015
                    $this->message[$pos]['type'] = $value_localpart;
7016
                    $this->message[$pos]['typePrefix'] = $value_prefix;
7017
                    if (isset($this->namespaces[$value_prefix]))
7018
                    {
7019
                        $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
7020
                    }
7021
                    elseif (isset($attrs['xmlns:' . $value_prefix]))
7022
                    {
7023
                        $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
7024
                    }
7025
                    // should do something here with the namespace of specified type?
7026
                }
7027
            } elseif ($key_localpart == 'arrayType') {
7028
                $this->message[$pos]['type'] = 'array';
7029
                /* do arrayType ereg here
7030
                [1]    arrayTypeValue    ::=    atype asize
7031
                [2]    atype    ::=    QName rank*
7032
                [3]    rank    ::=    '[' (',')* ']'
7033
                [4]    asize    ::=    '[' length~ ']'
7034
                [5]    length    ::=    nextDimension* Digit+
7035
                [6]    nextDimension    ::=    Digit+ ','
7036
                */
7037
                $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
7038
                if (preg_match($expr, $value, $regs)) {
7039
                    $this->message[$pos]['typePrefix'] = $regs[1];
7040
                    $this->message[$pos]['arrayTypePrefix'] = $regs[1];
7041
                    if (isset($this->namespaces[$regs[1]])) {
7042
                        $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
7043
                    } elseif (isset($attrs['xmlns:' . $regs[1]])) {
7044
                        $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:' . $regs[1]];
7045
                    }
7046
                    $this->message[$pos]['arrayType'] = $regs[2];
7047
                    $this->message[$pos]['arraySize'] = $regs[3];
7048
                    $this->message[$pos]['arrayCols'] = $regs[4];
7049
                }
7050
                // specifies nil value (or not)
7051
            } elseif ($key_localpart == 'nil') {
7052
                $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
7053
                // some other attribute
7054
            } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
7055
                $this->message[$pos]['xattrs']['!' . $key] = $value;
7056
            }
7057
7058
            if ($key == 'xmlns') {
7059
                $this->default_namespace = $value;
7060
            }
7061
            // log id
7062
            if ($key == 'id') {
7063
                $this->ids[$value] = $pos;
7064
            }
7065
            // root
7066
            if ($key_localpart == 'root' && $value == 1) {
7067
                $this->status = 'method';
7068
                $this->root_struct_name = $name;
7069
                $this->root_struct = $pos;
7070
                $this->debug("found root struct $this->root_struct_name, pos $pos");
7071
            }
7072
            // for doclit
7073
            $attstr .= " $key=\"$value\"";
7074
        }
7075
        // get namespace - must be done after namespace atts are processed
7076
        if (isset($prefix)) {
7077
            $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
7078
            $this->default_namespace = $this->namespaces[$prefix];
7079
        } else {
7080
            $this->message[$pos]['namespace'] = $this->default_namespace;
7081
        }
7082
        if ($this->status == 'header') {
7083
            if ($this->root_header != $pos) {
7084
                $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
7085
            }
7086
        } elseif ($this->root_struct_name != '') {
7087
            $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
7088
        }
7089
    }
7090
7091
    /**
7092
     * end-element handler
7093
     *
7094
     * @param    resource $parser XML parser object
7095
     * @param    string $name element name
7096
     * @access   private
7097
     */
7098
    function end_element($parser, $name)
7099
    {
7100
        // position of current element is equal to the last value left in depth_array for my depth
7101
        $pos = $this->depth_array[$this->depth--];
7102
7103
        // get element prefix
7104
        if (strpos($name, ':')) {
7105
            // get ns prefix
7106
            $prefix = substr($name, 0, strpos($name, ':'));
7107
            // get unqualified name
7108
            $name = substr(strstr($name, ':'), 1);
7109
        }
7110
7111
        // build to native type
7112
        if (isset($this->body_position) && $pos > $this->body_position) {
7113
            // deal w/ multirefs
7114
            if (isset($this->message[$pos]['attrs']['href'])) {
7115
                // get id
7116
                $id = substr($this->message[$pos]['attrs']['href'], 1);
7117
                // add placeholder to href array
7118
                $this->multirefs[$id][$pos] = 'placeholder';
7119
                // add set a reference to it as the result value
7120
                $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
7121
                // build complexType values
7122
            } elseif ($this->message[$pos]['children'] != '') {
7123
                // if result has already been generated (struct/array)
7124
                if (!isset($this->message[$pos]['result'])) {
7125
                    $this->message[$pos]['result'] = $this->buildVal($pos);
7126
                }
7127
                // build complexType values of attributes and possibly simpleContent
7128
            } elseif (isset($this->message[$pos]['xattrs'])) {
7129
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
7130
                    $this->message[$pos]['xattrs']['!'] = null;
7131
                } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
7132
                    if (isset($this->message[$pos]['type'])) {
7133
                        $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7134
                    } else {
7135
                        $parent = $this->message[$pos]['parent'];
7136
                        if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7137
                            $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7138
                        } else {
7139
                            $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
7140
                        }
7141
                    }
7142
                }
7143
                $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
7144
                // set value of simpleType (or nil complexType)
7145
            } else {
7146
                //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
7147
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
7148
                    $this->message[$pos]['xattrs']['!'] = null;
7149
                } elseif (isset($this->message[$pos]['type'])) {
7150
                    $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7151
                } else {
7152
                    $parent = $this->message[$pos]['parent'];
7153
                    if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7154
                        $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7155
                    } else {
7156
                        $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
7157
                    }
7158
                }
7159
7160
                /* add value to parent's result, if parent is struct/array
7161
                $parent = $this->message[$pos]['parent'];
7162
                if($this->message[$parent]['type'] != 'map'){
7163
                    if(strtolower($this->message[$parent]['type']) == 'array'){
7164
                        $this->message[$parent]['result'][] = $this->message[$pos]['result'];
7165
                    } else {
7166
                        $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
7167
                    }
7168
                }
7169
                */
7170
            }
7171
        }
7172
7173
        // for doclit
7174
        if ($this->status == 'header') {
7175
            if ($this->root_header != $pos) {
7176
                $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
7177
            }
7178
        } elseif ($pos >= $this->root_struct) {
7179
            $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
7180
        }
7181
        // switch status
7182
        if ($pos == $this->root_struct) {
7183
            $this->status = 'body';
7184
            $this->root_struct_namespace = $this->message[$pos]['namespace'];
7185
        } elseif ($pos == $this->root_header) {
7186
            $this->status = 'envelope';
7187
        } elseif ($name == 'Body' && $this->status == 'body') {
7188
            $this->status = 'envelope';
7189
        } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
7190
            $this->status = 'envelope';
7191
        } elseif ($name == 'Envelope' && $this->status == 'envelope') {
7192
            $this->status = '';
7193
        }
7194
        // set parent back to my parent
7195
        $this->parent = $this->message[$pos]['parent'];
7196
    }
7197
7198
    /**
7199
     * element content handler
7200
     *
7201
     * @param    resource $parser XML parser object
7202
     * @param    string $data element content
7203
     * @access   private
7204
     */
7205
    function character_data($parser, $data)
7206
    {
7207
        $pos = $this->depth_array[$this->depth];
7208
        if ($this->xml_encoding == 'UTF-8') {
7209
            // TODO: add an option to disable this for folks who want
7210
            // raw UTF-8 that, e.g., might not map to iso-8859-1
7211
            // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
7212
            if ($this->decode_utf8) {
7213
                $data = function_exists('mb_convert_encoding') ? mb_convert_encoding($data, 'ISO-8859-1', 'UTF-8') : utf8_decode($data);
7214
            }
7215
        }
7216
        $this->message[$pos]['cdata'] .= $data;
7217
        // for doclit
7218
        if ($this->status == 'header') {
7219
            $this->responseHeaders .= $data;
7220
        } else {
7221
            $this->document .= $data;
7222
        }
7223
    }
7224
7225
    /**
7226
     * get the parsed message (SOAP Body)
7227
     *
7228
     * @return    mixed
7229
     * @access   public
7230
     * @deprecated    use get_soapbody instead
7231
     */
7232
    function get_response()
7233
    {
7234
        return $this->soapresponse;
7235
    }
7236
7237
    /**
7238
     * get the parsed SOAP Body (null if there was none)
7239
     *
7240
     * @return    mixed
7241
     * @access   public
7242
     */
7243
    function get_soapbody()
7244
    {
7245
        return $this->soapresponse;
7246
    }
7247
7248
    /**
7249
     * get the parsed SOAP Header (null if there was none)
7250
     *
7251
     * @return    mixed
7252
     * @access   public
7253
     */
7254
    function get_soapheader()
7255
    {
7256
        return $this->soapheader;
7257
    }
7258
7259
    /**
7260
     * get the unparsed SOAP Header
7261
     *
7262
     * @return    string XML or empty if no Header
7263
     * @access   public
7264
     */
7265
    function getHeaders()
7266
    {
7267
        return $this->responseHeaders;
7268
    }
7269
7270
    /**
7271
     * decodes simple types into PHP variables
7272
     *
7273
     * @param    string $value value to decode
7274
     * @param    string $type XML type to decode
7275
     * @param    string $typens XML type namespace to decode
7276
     * @return    mixed PHP value
7277
     * @access   private
7278
     */
7279
    function decodeSimple($value, $type, $typens)
7280
    {
7281
        // TODO: use the namespace!
7282
        if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
7283
            return (string) $value;
7284
        }
7285
        if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
7286
            return (int) $value;
7287
        }
7288
        if ($type == 'float' || $type == 'double' || $type == 'decimal') {
7289
            return (double) $value;
7290
        }
7291
        if ($type == 'boolean') {
7292
            if (strtolower($value) == 'false' || strtolower($value) == 'f') {
7293
                return false;
7294
            }
7295
            return (bool) $value;
7296
        }
7297
        if ($type == 'base64' || $type == 'base64Binary') {
7298
            $this->debug('Decode base64 value');
7299
            return base64_decode($value);
7300
        }
7301
        // obscure numeric types
7302
        if (
7303
            $type == 'nonPositiveInteger' || $type == 'negativeInteger'
7304
            || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
7305
            || $type == 'unsignedInt'
7306
            || $type == 'unsignedShort' || $type == 'unsignedByte'
7307
        ) {
7308
            return (int) $value;
7309
        }
7310
        // bogus: parser treats array with no elements as a simple type
7311
        if ($type == 'array') {
7312
            return array();
7313
        }
7314
        // everything else
7315
        return (string) $value;
7316
    }
7317
7318
    /**
7319
     * builds response structures for compound values (arrays/structs)
7320
     * and scalars
7321
     *
7322
     * @param    integer $pos position in node tree
7323
     * @return    mixed    PHP value
7324
     * @access   private
7325
     */
7326
    function buildVal($pos)
7327
    {
7328
        if (!isset($this->message[$pos]['type'])) {
7329
            $this->message[$pos]['type'] = '';
7330
        }
7331
        $this->debug('in buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
7332
        // if there are children...
7333
        if ($this->message[$pos]['children'] != '') {
7334
            $params = [];
7335
            $this->debug('in buildVal, there are children');
7336
            $children = explode('|', $this->message[$pos]['children']);
7337
            array_shift($children); // knock off empty
7338
            // md array
7339
            if (isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != '') {
7340
                $r = 0; // rowcount
7341
                $c = 0; // colcount
7342
                foreach ($children as $child_pos) {
7343
                    $this->debug("in buildVal, got an MD array element: $r, $c");
7344
                    $params[$r][] = $this->message[$child_pos]['result'];
7345
                    $c++;
7346
                    if ($c == $this->message[$pos]['arrayCols']) {
7347
                        $c = 0;
7348
                        $r++;
7349
                    }
7350
                }
7351
                // array
7352
            } elseif ($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array') {
7353
                $this->debug('in buildVal, adding array ' . $this->message[$pos]['name']);
7354
                foreach ($children as $child_pos) {
7355
                    $params[] = &$this->message[$child_pos]['result'];
7356
                }
7357
                // apache Map type: java hashtable
7358
            } elseif ($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
7359
                $this->debug('in buildVal, Java Map ' . $this->message[$pos]['name']);
7360
                foreach ($children as $child_pos) {
7361
                    $kv = explode("|", $this->message[$child_pos]['children']);
7362
                    $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
7363
                }
7364
                // generic compound type
7365
                //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
7366
            } else {
7367
                // Apache Vector type: treat as an array
7368
                $this->debug('in buildVal, adding Java Vector or generic compound type ' . $this->message[$pos]['name']);
7369
                if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
7370
                    $notstruct = 1;
7371
                } else {
7372
                    $notstruct = 0;
7373
                }
7374
                //
7375
                foreach ($children as $child_pos) {
7376
                    if ($notstruct) {
7377
                        $params[] = &$this->message[$child_pos]['result'];
7378
                    } else {
7379
                        if (isset($params[$this->message[$child_pos]['name']])) {
7380
                            // de-serialize repeated element name into an array
7381
                            if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
7382
                                $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
7383
                            }
7384
                            $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
7385
                        } else {
7386
                            $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
7387
                        }
7388
                    }
7389
                }
7390
            }
7391
            if (isset($this->message[$pos]['xattrs'])) {
7392
                $this->debug('in buildVal, handling attributes');
7393
                foreach ($this->message[$pos]['xattrs'] as $n => $v) {
7394
                    $params[$n] = $v;
7395
                }
7396
            }
7397
            // handle simpleContent
7398
            if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
7399
                $this->debug('in buildVal, handling simpleContent');
7400
                if (isset($this->message[$pos]['type'])) {
7401
                    $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7402
                } else {
7403
                    $parent = $this->message[$pos]['parent'];
7404
                    if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7405
                        $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7406
                    } else {
7407
                        $params['!'] = $this->message[$pos]['cdata'];
7408
                    }
7409
                }
7410
            }
7411
            $ret = is_array($params) ? $params : array();
7412
            $this->debug('in buildVal, return:');
7413
            $this->appendDebug($this->varDump($ret));
7414
        } else {
7415
            $this->debug('in buildVal, no children, building scalar');
7416
            $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
7417
            if (isset($this->message[$pos]['type'])) {
7418
                $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7419
                $this->debug("in buildVal, return: $ret");
7420
                return $ret;
7421
            }
7422
            $parent = $this->message[$pos]['parent'];
7423
            if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7424
                $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7425
                $this->debug("in buildVal, return: $ret");
7426
                return $ret;
7427
            }
7428
            $ret = $this->message[$pos]['cdata'];
7429
            $this->debug("in buildVal, return: $ret");
7430
        }
7431
7432
        return $ret;
7433
    }
7434
}
7435
7436
7437
/**
7438
 * Backward compatibility
7439
 */
7440
class soap_parser extends nusoap_parser
7441
{
7442
}
7443
7444
7445
/**
7446
 *
7447
 * [nu]soapclient higher level class for easy usage.
7448
 *
7449
 * usage:
7450
 *
7451
 * // instantiate client with server info
7452
 * $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
7453
 *
7454
 * // call method, get results
7455
 * echo $soapclient->call( string methodname [ ,array parameters] );
7456
 *
7457
 * // bye bye client
7458
 * unset($soapclient);
7459
 *
7460
 * @author   Dietrich Ayala <[email protected]>
7461
 * @author   Scott Nichol <[email protected]>
7462
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
7463
 * @access   public
7464
 */
7465
class nusoap_client extends nusoap_base
7466
{
7467
    var $attachments = '';
7468
    var $return = null;
7469
    var $operation = '';
7470
    var $opData = array();
7471
    var $username = '';                // Username for HTTP authentication
7472
    var $password = '';                // Password for HTTP authentication
7473
    var $authtype = '';                // Type of HTTP authentication
7474
    var $certRequest = array();        // Certificate for HTTP SSL authentication
7475
    var $requestHeaders = false;    // SOAP headers in request (text)
7476
    var $responseHeaders = '';        // SOAP headers from response (incomplete namespace resolution) (text)
7477
    var $responseHeader = null;        // SOAP Header from response (parsed)
7478
    var $document = '';                // SOAP body response portion (incomplete namespace resolution) (text)
7479
    var $endpoint;
7480
    var $forceEndpoint = '';        // overrides WSDL endpoint
7481
    var $proxyhost = '';
7482
    var $proxyport = '';
7483
    var $proxyusername = '';
7484
    var $proxypassword = '';
7485
    var $portName = '';                // port name to use in WSDL
7486
    var $xml_encoding = '';            // character set encoding of incoming (response) messages
7487
    var $http_encoding = false;
7488
    var $timeout = 0;                // HTTP connection timeout
7489
    var $response_timeout = 30;        // HTTP response timeout
7490
    var $endpointType = '';            // soap|wsdl, empty for WSDL initialization error
7491
    var $persistentConnection = false;
7492
    var $defaultRpcParams = false;    // This is no longer used
7493
    var $request = '';                // HTTP request
7494
    var $response = '';                // HTTP response
7495
    var $responseData = '';            // SOAP payload of response
7496
    var $cookies = array();            // Cookies from response or for request
7497
    var $decode_utf8 = true;        // toggles whether the parser decodes element content w/ utf8_decode()
7498
    var $operations = array();        // WSDL operations, empty for WSDL initialization error
7499
    var $curl_options = array();    // User-specified cURL options
7500
    var $bindingType = '';            // WSDL operation binding type
7501
    var $use_curl = false;            // whether to always try to use cURL
7502
7503
    /*
7504
     * fault related variables
7505
     */
7506
    /**
7507
     * @var      bool
7508
     * @access   public
7509
     */
7510
    var $fault;
7511
    /**
7512
     * @var      string
7513
     * @access   public
7514
     */
7515
    var $faultcode;
7516
    /**
7517
     * @var      string
7518
     * @access   public
7519
     */
7520
    var $faultstring;
7521
    /**
7522
     * @var      string
7523
     * @access   public
7524
     */
7525
    var $faultdetail;
7526
7527
    /** @var wsdl|null */
7528
    var $wsdl;
7529
    /** @var mixed */
7530
    var $wsdlFile;
7531
7532
    /**
7533
     * constructor
7534
     *
7535
     * @param    mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
7536
     * @param    mixed $wsdl optional, set to 'wsdl' or true if using WSDL
7537
     * @param    string $proxyhost optional
7538
     * @param    string $proxyport optional
7539
     * @param    string $proxyusername optional
7540
     * @param    string $proxypassword optional
7541
     * @param    integer $timeout set the connection timeout
7542
     * @param    integer $response_timeout set the response timeout
7543
     * @param    string $portName optional portName in WSDL document
7544
     * @access   public
7545
     */
7546
    function __construct($endpoint, $wsdl = false, $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = '')
7547
    {
7548
        parent::__construct();
7549
        $this->endpoint = $endpoint;
7550
        $this->proxyhost = $proxyhost;
7551
        $this->proxyport = $proxyport;
7552
        $this->proxyusername = $proxyusername;
7553
        $this->proxypassword = $proxypassword;
7554
        $this->timeout = $timeout;
7555
        $this->response_timeout = $response_timeout;
7556
        $this->portName = $portName;
7557
7558
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7559
        $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7560
7561
        // make values
7562
        if ($wsdl) {
7563
            if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
7564
                $this->wsdl = $endpoint;
7565
                $this->endpoint = $this->wsdl->wsdl;
7566
                $this->wsdlFile = $this->endpoint;
7567
                $this->debug('existing wsdl instance created from ' . $this->endpoint);
7568
                $this->checkWSDL();
7569
            } else {
7570
                $this->wsdlFile = $this->endpoint;
7571
                $this->wsdl = null;
7572
                $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
7573
            }
7574
            $this->endpointType = 'wsdl';
7575
        } else {
7576
            $this->debug("instantiate SOAP with endpoint at $endpoint");
7577
            $this->endpointType = 'soap';
7578
        }
7579
    }
7580
7581
    /**
7582
     * calls method, returns PHP native type
7583
     *
7584
     * @param    string $operation SOAP server URL or path
7585
     * @param    mixed $params An array, associative or simple, of the parameters
7586
     *                          for the method call, or a string that is the XML
7587
     *                          for the call.  For rpc style, this call will
7588
     *                          wrap the XML in a tag named after the method, as
7589
     *                          well as the SOAP Envelope and Body.  For document
7590
     *                          style, this will only wrap with the Envelope and Body.
7591
     *                          IMPORTANT: when using an array with document style,
7592
     *                          in which case there
7593
     *                         is really one parameter, the root of the fragment
7594
     *                         used in the call, which encloses what programmers
7595
     *                         normally think of parameters.  A parameter array
7596
     *                         *must* include the wrapper.
7597
     * @param    string $namespace optional method namespace (WSDL can override)
7598
     * @param    string $soapAction optional SOAPAction value (WSDL can override)
7599
     * @param    mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7600
     * @param    boolean $rpcParams optional (no longer used)
7601
     * @param    string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7602
     * @param    string $use optional (encoded|literal|literal wrapped) the use when serializing parameters (WSDL can override)
7603
     * @return    mixed    response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
7604
     * @access   public
7605
     */
7606
    function call($operation, $params = array(), $namespace = 'http://tempuri.org', $soapAction = '', $headers = false, $rpcParams = null, $style = 'rpc', $use = 'encoded')
7607
    {
7608
        $this->operation = $operation;
7609
        $this->fault = false;
7610
        $this->setError('');
7611
        $this->request = '';
7612
        $this->response = '';
7613
        $this->responseData = '';
7614
        $this->faultstring = '';
7615
        $this->faultcode = '';
7616
        $this->opData = array();
7617
7618
        $usewrapped = false;
7619
7620
        $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7621
        $this->appendDebug('params=' . $this->varDump($params));
7622
        $this->appendDebug('headers=' . $this->varDump($headers));
7623
        if ($headers) {
7624
            $this->requestHeaders = $headers;
7625
        }
7626
        if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7627
            $this->loadWSDL();
7628
            if ($this->getError()) {
7629
                return false;
7630
            }
7631
        }
7632
        // serialize parameters
7633
        if ($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)) {
7634
            // use WSDL for operation
7635
            $this->opData = $opData;
7636
            $this->debug("found operation");
7637
            $this->appendDebug('opData=' . $this->varDump($opData));
7638
            if (isset($opData['soapAction'])) {
7639
                $soapAction = $opData['soapAction'];
7640
            }
7641
            if (!$this->forceEndpoint) {
7642
                $this->endpoint = $opData['endpoint'];
7643
            } else {
7644
                $this->endpoint = $this->forceEndpoint;
7645
            }
7646
            $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7647
            $style = $opData['style'];
7648
            $use = $opData['input']['use'];
7649
            // add ns to ns array
7650
            if ($namespace != '' && !isset($this->wsdl->namespaces[$namespace])) {
7651
                $nsPrefix = 'ns' . rand(1000, 9999);
7652
                $this->wsdl->namespaces[$nsPrefix] = $namespace;
7653
            }
7654
            $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7655
            // serialize payload
7656
            if (is_string($params)) {
7657
                $this->debug("serializing param string for WSDL operation $operation");
7658
                $payload = $params;
7659
            } elseif (is_array($params)) {
7660
                $this->debug("serializing param array for WSDL operation $operation");
7661
                $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params, $this->bindingType);
7662
            } else {
7663
                $this->debug('params must be array or string');
7664
                $this->setError('params must be array or string');
7665
                return false;
7666
            }
7667
            $usedNamespaces = $this->wsdl->usedNamespaces;
7668
            if (isset($opData['input']['encodingStyle'])) {
7669
                $encodingStyle = $opData['input']['encodingStyle'];
7670
            } else {
7671
                $encodingStyle = '';
7672
            }
7673
            $this->appendDebug($this->wsdl->getDebug());
7674
            $this->wsdl->clearDebug();
7675
            if ($errstr = $this->wsdl->getError()) {
7676
                $this->debug('got wsdl error: ' . $errstr);
7677
                $this->setError('wsdl error: ' . $errstr);
7678
                return false;
7679
            }
7680
        } elseif ($this->endpointType == 'wsdl') {
7681
            // operation not in WSDL
7682
            $this->appendDebug($this->wsdl->getDebug());
7683
            $this->wsdl->clearDebug();
7684
            $this->setError('operation ' . $operation . ' not present in WSDL.');
7685
            $this->debug("operation '$operation' not present in WSDL.");
7686
            return false;
7687
        } else {
7688
            // no WSDL
7689
            //$this->namespaces['ns1'] = $namespace;
7690
            $nsPrefix = 'ns' . rand(1000, 9999);
7691
            // serialize
7692
            $payload = '';
7693
7694
            if ($use == 'literal wrapped') {
7695
                // 'literal wrapped' is only sensible (and defined) for 'document'.
7696
                if ($style == 'document') {
7697
                    $usewrapped = true;
7698
                }
7699
                // For compatibility with the rest of the code:
7700
                $use = 'literal';
7701
            }
7702
7703
            if (is_string($params)) {
7704
                $this->debug("serializing param string for operation $operation");
7705
                $payload = $params;
7706
            } elseif (is_array($params)) {
7707
                $this->debug("serializing param array for operation $operation");
7708
                foreach ($params as $k => $v) {
7709
                    $payload .= $this->serialize_val($v, $k, false, false, false, false, $use);
7710
                }
7711
            } else {
7712
                $this->debug('params must be array or string');
7713
                $this->setError('params must be array or string');
7714
                return false;
7715
            }
7716
            $usedNamespaces = array();
7717
            if ($use == 'encoded') {
7718
                $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7719
            } else {
7720
                $encodingStyle = '';
7721
            }
7722
        }
7723
7724
        // wrap document/literal wrapped calls with operation element
7725
        if ($usewrapped) {
7726
            // (This code block was based on http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/
7727
            // and tailored to the needs of one specific SOAP server, where no nsPrefix was seen...
7728
            $this->debug("wrapping document request with literal method element");
7729
7730
            if ($namespace) {
7731
                $payload = "<$operation xmlns=\"$namespace\">" .
7732
                    $payload .
7733
                    "</$operation>";
7734
            } else {
7735
                $payload = "<$operation>" . $payload . "</$operation>";
7736
            }
7737
        }
7738
7739
        // wrap RPC calls with method element
7740
        if ($style == 'rpc') {
7741
            if ($use == 'literal') {
7742
                $this->debug("wrapping RPC request with literal method element");
7743
                if ($namespace) {
7744
                    // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
7745
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7746
                        $payload .
7747
                        "</$nsPrefix:$operation>";
7748
                } else {
7749
                    $payload = "<$operation>" . $payload . "</$operation>";
7750
                }
7751
            } else {
7752
                $this->debug("wrapping RPC request with encoded method element");
7753
                if ($namespace) {
7754
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7755
                        $payload .
7756
                        "</$nsPrefix:$operation>";
7757
                } else {
7758
                    $payload = "<$operation>" .
7759
                        $payload .
7760
                        "</$operation>";
7761
                }
7762
            }
7763
        }
7764
        // serialize envelope
7765
        $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $usedNamespaces, $style, $use, $encodingStyle);
7766
        $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7767
        $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
7768
        // send
7769
        $return = $this->send($this->getHTTPBody($soapmsg), $soapAction, $this->timeout, $this->response_timeout);
7770
        if ($errstr = $this->getError()) {
7771
            $this->debug('Error: ' . $errstr);
7772
            return false;
7773
        } else {
7774
            $this->return = $return;
7775
            $this->debug('sent message successfully and got a(n) ' . gettype($return));
7776
            $this->appendDebug('return=' . $this->varDump($return));
7777
7778
            // fault?
7779
            if (is_array($return) && isset($return['faultcode'])) {
7780
                $this->debug('got fault');
7781
                $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
7782
                $this->fault = true;
7783
                foreach ($return as $k => $v) {
7784
                    $this->$k = $v;
7785
                    if (is_array($v)) {
7786
                        $this->debug("$k = " . json_encode($v));
7787
                    } else {
7788
                        $this->debug("$k = $v<br>");
7789
                    }
7790
                }
7791
                return $return;
7792
            } elseif ($style == 'document') {
7793
                // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7794
                // we are only going to return the first part here...sorry about that
7795
                return $return;
7796
            } else {
7797
                // array of return values
7798
                if (is_array($return)) {
7799
                    // multiple 'out' parameters, which we return wrapped up
7800
                    // in the array
7801
                    if (sizeof($return) > 1) {
7802
                        return $return;
7803
                    }
7804
                    // single 'out' parameter (normally the return value)
7805
                    $return = array_shift($return);
7806
                    $this->debug('return shifted value: ');
7807
                    $this->appendDebug($this->varDump($return));
7808
                    return $return;
7809
                    // nothing returned (ie, echoVoid)
7810
                } else {
7811
                    return "";
7812
                }
7813
            }
7814
        }
7815
    }
7816
7817
    /**
7818
     * check WSDL passed as an instance or pulled from an endpoint
7819
     *
7820
     * @access   private
7821
     */
7822
    function checkWSDL()
7823
    {
7824
        $this->appendDebug($this->wsdl->getDebug());
7825
        $this->wsdl->clearDebug();
7826
        $this->debug('checkWSDL');
7827
        // catch errors
7828
        if ($errstr = $this->wsdl->getError()) {
7829
            $this->appendDebug($this->wsdl->getDebug());
7830
            $this->wsdl->clearDebug();
7831
            $this->debug('got wsdl error: ' . $errstr);
7832
            $this->setError('wsdl error: ' . $errstr);
7833
        } elseif ($this->operations = $this->wsdl->getOperations($this->portName)) {
7834
            $this->appendDebug($this->wsdl->getDebug());
7835
            $this->wsdl->clearDebug();
7836
            $this->bindingType = 'soap';
7837
            $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7838
        } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
7839
            $this->appendDebug($this->wsdl->getDebug());
7840
            $this->wsdl->clearDebug();
7841
            $this->bindingType = 'soap12';
7842
            $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7843
            $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7844
        } else {
7845
            $this->appendDebug($this->wsdl->getDebug());
7846
            $this->wsdl->clearDebug();
7847
            $this->debug('getOperations returned false');
7848
            $this->setError('no operations defined in the WSDL document!');
7849
        }
7850
    }
7851
7852
    /**
7853
     * instantiate wsdl object and parse wsdl file
7854
     *
7855
     * @access    public
7856
     */
7857
    function loadWSDL()
7858
    {
7859
        $this->debug('instantiating wsdl class with doc: ' . $this->wsdlFile);
7860
        $this->wsdl = new wsdl('', $this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword, $this->timeout, $this->response_timeout, $this->curl_options, $this->use_curl);
7861
        $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7862
        $this->wsdl->fetchWSDL($this->wsdlFile);
7863
        $this->checkWSDL();
7864
    }
7865
7866
    /**
7867
     * get available data pertaining to an operation
7868
     *
7869
     * @param    string $operation operation name
7870
     * @return   array|false array of data pertaining to the operation, false on error or no data
7871
     * @access   public
7872
     */
7873
    function getOperationData($operation)
7874
    {
7875
        if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7876
            $this->loadWSDL();
7877
            if ($this->getError()) {
7878
                return false;
7879
            }
7880
        }
7881
        if (isset($this->operations[$operation])) {
7882
            return $this->operations[$operation];
7883
        }
7884
        $this->debug("No data for operation: $operation");
7885
        return false;
7886
    }
7887
7888
    /**
7889
     * send the SOAP message
7890
     *
7891
     * Note: if the operation has multiple return values
7892
     * the return value of this method will be an array
7893
     * of those values.
7894
     *
7895
     * @param    string $msg a SOAPx4 soapmsg object
7896
     * @param    string $soapaction SOAPAction value
7897
     * @param    integer $timeout set connection timeout in seconds
7898
     * @param    integer $response_timeout set response timeout in seconds
7899
     * @return    mixed native PHP types.
7900
     * @access   private
7901
     */
7902
    function send($msg, $soapaction = '', $timeout = 0, $response_timeout = 30)
7903
    {
7904
        $this->checkCookies();
7905
        // detect transport
7906
        switch (true) {
7907
            // http(s)
7908
            case preg_match('/^http/', $this->endpoint):
7909
                $this->debug('transporting via HTTP');
7910
                if ($this->persistentConnection && is_object($this->persistentConnection)) {
7911
                    $http =& $this->persistentConnection;
7912
                } else {
7913
                    $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7914
                    if ($this->persistentConnection) {
7915
                        $http->usePersistentConnection();
7916
                    }
7917
                }
7918
                $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7919
                $http->setSOAPAction($soapaction);
7920
                if ($this->proxyhost && $this->proxyport) {
7921
                    $http->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
7922
                }
7923
                if ($this->authtype != '') {
7924
                    $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
7925
                }
7926
                if ($this->http_encoding != '') {
7927
                    $http->setEncoding($this->http_encoding);
7928
                }
7929
                $this->debug('sending message, length=' . strlen($msg));
7930
                if (preg_match('/^http:/', $this->endpoint)) {
7931
                    //if(strpos($this->endpoint,'http:')){
7932
                    $this->responseData = $http->send($msg, $timeout, $response_timeout, $this->cookies);
7933
                } elseif (preg_match('/^https/', $this->endpoint)) {
7934
                    //} elseif(strpos($this->endpoint,'https:')){
7935
                    //if(phpversion() == '4.3.0-dev'){
7936
                    //$response = $http->send($msg,$timeout,$response_timeout);
7937
                    //$this->request = $http->outgoing_payload;
7938
                    //$this->response = $http->incoming_payload;
7939
                    //} else
7940
                    $this->responseData = $http->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
7941
                } else {
7942
                    $this->setError('no http/s in endpoint url');
7943
                }
7944
                $this->request = $http->outgoing_payload;
7945
                $this->response = $http->incoming_payload;
7946
                $this->appendDebug($http->getDebug());
7947
                $this->UpdateCookies($http->incoming_cookies);
7948
7949
                // save transport object if using persistent connections
7950
                if ($this->persistentConnection) {
7951
                    $http->clearDebug();
7952
                    if (!is_object($this->persistentConnection)) {
7953
                        $this->persistentConnection = $http;
7954
                    }
7955
                }
7956
7957
                if ($err = $http->getError()) {
7958
                    $this->setError('HTTP Error: ' . $err);
7959
                    return false;
7960
                } elseif ($this->getError()) {
7961
                    return false;
7962
                } else {
7963
                    $this->debug('got response, length=' . strlen($this->responseData) . ' type=' . $http->incoming_headers['content-type']);
7964
                    return $this->parseResponse($http->incoming_headers, $this->responseData);
7965
                }
7966
            default:
7967
                $this->setError('no transport found, or selected transport is not yet supported!');
7968
                return false;
7969
        }
7970
    }
7971
7972
    /**
7973
     * processes SOAP message returned from server
7974
     *
7975
     * @param    array $headers The HTTP headers
7976
     * @param    string $data unprocessed response data from server
7977
     * @return    mixed    value of the message, decoded into a PHP type
7978
     * @access   private
7979
     */
7980
    function parseResponse($headers, $data)
7981
    {
7982
        $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
7983
        $this->appendDebug($this->varDump($headers));
7984
        if (!isset($headers['content-type'])) {
7985
            $this->setError('Response not of type ' . $this->contentType . ' (no content-type header)');
7986
            return false;
7987
        }
7988
        if (!strstr($headers['content-type'], $this->contentType)) {
7989
            $this->setError('Response not of type ' . $this->contentType . ': ' . $headers['content-type']);
7990
            return false;
7991
        }
7992
        if (strpos($headers['content-type'], '=')) {
7993
            $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
7994
            $this->debug('Got response encoding: ' . $enc);
7995
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
7996
                $this->xml_encoding = strtoupper($enc);
7997
            } else {
7998
                $this->xml_encoding = 'US-ASCII';
7999
            }
8000
        } else {
8001
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
8002
            $this->xml_encoding = 'ISO-8859-1';
8003
        }
8004
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
8005
        $parser = new nusoap_parser($data, $this->xml_encoding, $this->operations, $this->decode_utf8);
8006
        // add parser debug data to our debug
8007
        $this->appendDebug($parser->getDebug());
8008
        // if parse errors
8009
        if ($errstr = $parser->getError()) {
8010
            $this->setError($errstr);
8011
            // destroy the parser object
8012
            unset($parser);
8013
            return false;
8014
        } else {
8015
            // get SOAP headers
8016
            $this->responseHeaders = $parser->getHeaders();
8017
            // get SOAP headers
8018
            $this->responseHeader = $parser->get_soapheader();
8019
            // get decoded message
8020
            $return = $parser->get_soapbody();
8021
            // add document for doclit support
8022
            $this->document = $parser->document;
8023
            // Add attachments
8024
            $this->attachments = $parser->attachments;
8025
            // destroy the parser object
8026
            unset($parser);
8027
            // return decode message
8028
            return $return;
8029
        }
8030
    }
8031
8032
    /**
8033
     * sets user-specified cURL options
8034
     *
8035
     * @param    mixed $option The cURL option (always integer?)
8036
     * @param    mixed $value The cURL option value
8037
     * @access   public
8038
     */
8039
    function setCurlOption($option, $value)
8040
    {
8041
        $this->debug("setCurlOption option=$option, value=");
8042
        $this->appendDebug($this->varDump($value));
8043
        $this->curl_options[$option] = $value;
8044
    }
8045
8046
    /**
8047
     * sets the SOAP endpoint, which can override WSDL
8048
     *
8049
     * @param    string $endpoint The endpoint URL to use, or empty string or false to prevent override
8050
     * @access   public
8051
     */
8052
    function setEndpoint($endpoint)
8053
    {
8054
        $this->debug("setEndpoint(\"$endpoint\")");
8055
        $this->forceEndpoint = $endpoint;
8056
    }
8057
8058
    /**
8059
     * set the SOAP headers
8060
     *
8061
     * @param    mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
8062
     * @access   public
8063
     */
8064
    function setHeaders($headers)
8065
    {
8066
        $this->debug("setHeaders headers=");
8067
        $this->appendDebug($this->varDump($headers));
8068
        $this->requestHeaders = $headers;
8069
    }
8070
8071
    /**
8072
     * get the SOAP response headers (namespace resolution incomplete)
8073
     *
8074
     * @return    string
8075
     * @access   public
8076
     */
8077
    function getHeaders()
8078
    {
8079
        return $this->responseHeaders;
8080
    }
8081
8082
    /**
8083
     * get the SOAP response Header (parsed)
8084
     *
8085
     * @return    mixed
8086
     * @access   public
8087
     */
8088
    function getHeader()
8089
    {
8090
        return $this->responseHeader;
8091
    }
8092
8093
    /**
8094
     * set proxy info here
8095
     *
8096
     * @param    string $proxyhost
8097
     * @param    string $proxyport
8098
     * @param    string $proxyusername
8099
     * @param    string $proxypassword
8100
     * @access   public
8101
     */
8102
    function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
8103
    {
8104
        $this->proxyhost = $proxyhost;
8105
        $this->proxyport = $proxyport;
8106
        $this->proxyusername = $proxyusername;
8107
        $this->proxypassword = $proxypassword;
8108
    }
8109
8110
    /**
8111
     * if authenticating, set user credentials here
8112
     *
8113
     * @param    string $username
8114
     * @param    string $password
8115
     * @param    string $authtype (basic|digest|certificate|ntlm)
8116
     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
8117
     * @access   public
8118
     */
8119
    function setCredentials($username, $password, $authtype = 'basic', $certRequest = array())
8120
    {
8121
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
8122
        $this->appendDebug($this->varDump($certRequest));
8123
        $this->username = $username;
8124
        $this->password = $password;
8125
        $this->authtype = $authtype;
8126
        $this->certRequest = $certRequest;
8127
    }
8128
8129
    /**
8130
     * use HTTP encoding
8131
     *
8132
     * @param    string $enc HTTP encoding
8133
     * @access   public
8134
     */
8135
    function setHTTPEncoding($enc = 'gzip, deflate')
8136
    {
8137
        $this->debug("setHTTPEncoding(\"$enc\")");
8138
        $this->http_encoding = $enc;
8139
    }
8140
8141
    /**
8142
     * Set whether to try to use cURL connections if possible
8143
     *
8144
     * @param    boolean $use Whether to try to use cURL
8145
     * @access   public
8146
     */
8147
    function setUseCURL($use)
8148
    {
8149
        $this->debug("setUseCURL($use)");
8150
        $this->use_curl = $use;
8151
    }
8152
8153
    /**
8154
     * use HTTP persistent connections if possible
8155
     *
8156
     * @access   public
8157
     */
8158
    function useHTTPPersistentConnection()
8159
    {
8160
        $this->debug("useHTTPPersistentConnection");
8161
        $this->persistentConnection = true;
8162
    }
8163
8164
    /**
8165
     * gets the default RPC parameter setting.
8166
     * If true, default is that call params are like RPC even for document style.
8167
     * Each call() can override this value.
8168
     *
8169
     * This is no longer used.
8170
     *
8171
     * @return boolean
8172
     * @access public
8173
     * @deprecated
8174
     */
8175
    function getDefaultRpcParams()
8176
    {
8177
        return $this->defaultRpcParams;
8178
    }
8179
8180
    /**
8181
     * sets the default RPC parameter setting.
8182
     * If true, default is that call params are like RPC even for document style
8183
     * Each call() can override this value.
8184
     *
8185
     * This is no longer used.
8186
     *
8187
     * @param    boolean $rpcParams
8188
     * @access public
8189
     * @deprecated
8190
     */
8191
    function setDefaultRpcParams($rpcParams)
8192
    {
8193
        $this->defaultRpcParams = $rpcParams;
8194
    }
8195
8196
    /**
8197
     * dynamically creates an instance of a proxy class,
8198
     * allowing user to directly call methods from wsdl
8199
     *
8200
     * @return   object soap_proxy object
8201
     * @access   public
8202
     */
8203
    function getProxy()
8204
    {
8205
        $r = rand();
8206
        $evalStr = $this->_getProxyClassCode($r);
8207
        //$this->debug("proxy class: $evalStr");
8208
        if ($this->getError()) {
8209
            $this->debug("Error from _getProxyClassCode, so return null");
8210
            return null;
8211
        }
8212
        // eval the class
8213
        eval($evalStr);
8214
        // instantiate proxy object
8215
        /** @var nusoap_client $proxy */
8216
        eval("\$proxy = new nusoap_proxy_$r('');");
8217
        // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
8218
        $proxy->endpointType = 'wsdl';
8219
        $proxy->wsdlFile = $this->wsdlFile;
8220
        $proxy->wsdl = $this->wsdl;
8221
        $proxy->operations = $this->operations;
8222
        $proxy->defaultRpcParams = $this->defaultRpcParams;
8223
        // transfer other state
8224
        $proxy->soap_defencoding = $this->soap_defencoding;
8225
        $proxy->username = $this->username;
8226
        $proxy->password = $this->password;
8227
        $proxy->authtype = $this->authtype;
8228
        $proxy->certRequest = $this->certRequest;
8229
        $proxy->requestHeaders = $this->requestHeaders;
8230
        $proxy->endpoint = $this->endpoint;
8231
        $proxy->forceEndpoint = $this->forceEndpoint;
8232
        $proxy->proxyhost = $this->proxyhost;
8233
        $proxy->proxyport = $this->proxyport;
8234
        $proxy->proxyusername = $this->proxyusername;
8235
        $proxy->proxypassword = $this->proxypassword;
8236
        $proxy->http_encoding = $this->http_encoding;
8237
        $proxy->timeout = $this->timeout;
8238
        $proxy->response_timeout = $this->response_timeout;
8239
        $proxy->persistentConnection = &$this->persistentConnection;
8240
        $proxy->decode_utf8 = $this->decode_utf8;
8241
        $proxy->curl_options = $this->curl_options;
8242
        $proxy->bindingType = $this->bindingType;
8243
        $proxy->use_curl = $this->use_curl;
8244
        return $proxy;
8245
    }
8246
8247
    /**
8248
     * dynamically creates proxy class code
8249
     *
8250
     * @return   string PHP/NuSOAP code for the proxy class
8251
     * @access   private
8252
     */
8253
    function _getProxyClassCode($r)
8254
    {
8255
        $this->debug("in getProxy endpointType=$this->endpointType");
8256
        $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
8257
        if ($this->endpointType != 'wsdl') {
8258
            $evalStr = 'A proxy can only be created for a WSDL client';
8259
            $this->setError($evalStr);
8260
8261
          return "echo \"$evalStr\";";
8262
        }
8263
        if (is_null($this->wsdl)) {
8264
            $this->loadWSDL();
8265
            if ($this->getError()) {
8266
                return "echo \"" . $this->getError() . "\";";
8267
            }
8268
        }
8269
        $evalStr = '';
8270
        foreach ($this->operations as $operation => $opData) {
8271
            if ($operation != '') {
8272
                $paramStr = '';
8273
                $paramArrayStr = '';
8274
                // create param string and param comment string
8275
                if (sizeof($opData['input']['parts']) > 0) {
8276
                    $paramCommentStr = '';
8277
                    foreach ($opData['input']['parts'] as $name => $type) {
8278
                        $paramStr .= "\$$name, ";
8279
                        $paramArrayStr .= "'$name' => \$$name, ";
8280
                        $paramCommentStr .= "$type \$$name, ";
8281
                    }
8282
                    $paramStr = substr($paramStr, 0, strlen($paramStr) - 2);
8283
                    $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr) - 2);
8284
                    $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr) - 2);
8285
                } else {
8286
                    $paramCommentStr = 'void';
8287
                }
8288
                $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
8289
                $evalStr .= "// $paramCommentStr
8290
	function " . str_replace('.', '__', $operation) . "($paramStr) {
8291
		\$params = array($paramArrayStr);
8292
		return \$this->call('$operation', \$params, '" . $opData['namespace'] . "', '" . (isset($opData['soapAction']) ? $opData['soapAction'] : '') . "');
8293
	}
8294
	";
8295
                unset($paramStr);
8296
                unset($paramCommentStr);
8297
            }
8298
        }
8299
8300
      return 'class nusoap_proxy_' . $r . ' extends nusoap_client {
8301
' . $evalStr . '
8302
}';
8303
    }
8304
8305
    /**
8306
     * dynamically creates proxy class code
8307
     *
8308
     * @return   string PHP/NuSOAP code for the proxy class
8309
     * @access   public
8310
     */
8311
    function getProxyClassCode()
8312
    {
8313
        $r = rand();
8314
        return $this->_getProxyClassCode($r);
8315
    }
8316
8317
    /**
8318
     * gets the HTTP body for the current request.
8319
     *
8320
     * @param string $soapmsg The SOAP payload
8321
     * @return string The HTTP body, which includes the SOAP payload
8322
     * @access private
8323
     */
8324
    function getHTTPBody($soapmsg)
8325
    {
8326
        return $soapmsg;
8327
    }
8328
8329
    /**
8330
     * gets the HTTP content type for the current request.
8331
     *
8332
     * Note: getHTTPBody must be called before this.
8333
     *
8334
     * @return string the HTTP content type for the current request.
8335
     * @access private
8336
     */
8337
    function getHTTPContentType()
8338
    {
8339
        return $this->contentType;
8340
    }
8341
8342
    /**
8343
     * allows you to change the HTTP ContentType of the request.
8344
     *
8345
     * @param   string $contentTypeNew
8346
     * @return  void
8347
     */
8348
    function setHTTPContentType($contentTypeNew = "text/xml")
8349
    {
8350
        $this->contentType = $contentTypeNew;
8351
    }
8352
8353
    /**
8354
     * gets the HTTP content type charset for the current request.
8355
     * returns false for non-text content types.
8356
     *
8357
     * Note: getHTTPBody must be called before this.
8358
     *
8359
     * @return string the HTTP content type charset for the current request.
8360
     * @access private
8361
     */
8362
    function getHTTPContentTypeCharset()
8363
    {
8364
        return $this->soap_defencoding;
8365
    }
8366
8367
    /*
8368
    * whether or not parser should decode utf8 element content
8369
    *
8370
    * @return   always returns true
8371
    * @access   public
8372
    */
8373
    function decodeUTF8($bool)
8374
    {
8375
        $this->decode_utf8 = $bool;
8376
        return true;
8377
    }
8378
8379
    /**
8380
     * adds a new Cookie into $this->cookies array
8381
     *
8382
     * @param    string $name Cookie Name
8383
     * @param    string $value Cookie Value
8384
     * @return    boolean if cookie-set was successful returns true, else false
8385
     * @access    public
8386
     */
8387
    function setCookie($name, $value)
8388
    {
8389
        if (strlen($name) == 0) {
8390
            return false;
8391
        }
8392
        $this->cookies[] = array('name' => $name, 'value' => $value);
8393
        return true;
8394
    }
8395
8396
    /**
8397
     * gets all Cookies
8398
     *
8399
     * @return   array with all internal cookies
8400
     * @access   public
8401
     */
8402
    function getCookies()
8403
    {
8404
        return $this->cookies;
8405
    }
8406
8407
    /**
8408
     * checks all Cookies and delete those which are expired
8409
     *
8410
     * @return   boolean always return true
8411
     * @access   private
8412
     */
8413
    function checkCookies()
8414
    {
8415
        if (sizeof($this->cookies) == 0) {
8416
            return true;
8417
        }
8418
        $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
8419
        $curr_cookies = $this->cookies;
8420
        $this->cookies = array();
8421
        foreach ($curr_cookies as $cookie) {
8422
            if (!is_array($cookie)) {
8423
                $this->debug('Remove cookie that is not an array');
8424
                continue;
8425
            }
8426
            if ((isset($cookie['expires'])) && (!empty($cookie['expires']))) {
8427
                if (strtotime($cookie['expires']) > time()) {
8428
                    $this->cookies[] = $cookie;
8429
                } else {
8430
                    $this->debug('Remove expired cookie ' . $cookie['name']);
8431
                }
8432
            } else {
8433
                $this->cookies[] = $cookie;
8434
            }
8435
        }
8436
        $this->debug('checkCookie: ' . sizeof($this->cookies) . ' cookies left in array');
8437
        return true;
8438
    }
8439
8440
    /**
8441
     * updates the current cookies with a new set
8442
     *
8443
     * @param    array $cookies new cookies with which to update current ones
8444
     * @return    boolean always return true
8445
     * @access    private
8446
     */
8447
    function UpdateCookies($cookies)
8448
    {
8449
        if (sizeof($this->cookies) == 0) {
8450
            // no existing cookies: take whatever is new
8451
            if (sizeof($cookies) > 0) {
8452
                $this->debug('Setting new cookie(s)');
8453
                $this->cookies = $cookies;
8454
            }
8455
            return true;
8456
        }
8457
        if (sizeof($cookies) == 0) {
8458
            // no new cookies: keep what we've got
8459
            return true;
8460
        }
8461
        // merge
8462
        foreach ($cookies as $newCookie) {
8463
            if (!is_array($newCookie)) {
8464
                continue;
8465
            }
8466
            if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
8467
                continue;
8468
            }
8469
            $newName = $newCookie['name'];
8470
8471
            $found = false;
8472
            for ($i = 0; $i < count($this->cookies); $i++) {
8473
                $cookie = $this->cookies[$i];
8474
                if (!is_array($cookie)) {
8475
                    continue;
8476
                }
8477
                if (!isset($cookie['name'])) {
8478
                    continue;
8479
                }
8480
                if ($newName != $cookie['name']) {
8481
                    continue;
8482
                }
8483
                $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
8484
                $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
8485
                if ($newDomain != $domain) {
8486
                    continue;
8487
                }
8488
                $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
8489
                $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
8490
                if ($newPath != $path) {
8491
                    continue;
8492
                }
8493
                $this->cookies[$i] = $newCookie;
8494
                $found = true;
8495
                $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
8496
                break;
8497
            }
8498
            if (!$found) {
8499
                $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
8500
                $this->cookies[] = $newCookie;
8501
            }
8502
        }
8503
        return true;
8504
    }
8505
}
8506
8507
8508
if (!extension_loaded('soap')) {
8509
    /**
8510
     *    For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
8511
     */
8512
    class soapclient extends nusoap_client
8513
    {
8514
    }
8515
}
8516
8517
/**
8518
 * caches instances of the wsdl class
8519
 *
8520
 * @author   Scott Nichol <[email protected]>
8521
 * @author  Ingo Fischer <[email protected]>
8522
 * @version  $Id: class.wsdlcache.php,v 1.7 2007/04/17 16:34:03 snichol Exp $
8523
 * @access public
8524
 */
8525
class nusoap_wsdlcache
8526
{
8527
    /**
8528
     *  @var resource
8529
     *  @access private
8530
     */
8531
    var $fplock;
8532
    /**
8533
     *  @var integer
8534
     *  @access private
8535
     */
8536
    var $cache_lifetime;
8537
    /**
8538
     *  @var string
8539
     *  @access private
8540
     */
8541
    var $cache_dir;
8542
    /**
8543
     *  @var string
8544
     *  @access public
8545
     */
8546
    var $debug_str = '';
8547
8548
    /**
8549
     * constructor
8550
     *
8551
     * @param string $cache_dir directory for cache-files
8552
     * @param integer $cache_lifetime lifetime for caching-files in seconds or 0 for unlimited
8553
     * @access public
8554
     */
8555
    function __construct($cache_dir = '.', $cache_lifetime = 0)
8556
    {
8557
        $this->fplock = array();
8558
        $this->cache_dir = $cache_dir != '' ? $cache_dir : '.';
8559
        $this->cache_lifetime = $cache_lifetime;
8560
    }
8561
8562
    /**
8563
     * creates the filename used to cache a wsdl instance
8564
     *
8565
     * @param string $wsdl The URL of the wsdl instance
8566
     * @return string The filename used to cache the instance
8567
     * @access private
8568
     */
8569
    function createFilename($wsdl)
8570
    {
8571
        return $this->cache_dir . '/wsdlcache-' . md5($wsdl);
8572
    }
8573
8574
    /**
8575
     * adds debug data to the class level debug string
8576
     *
8577
     * @param    string $string debug data
8578
     * @access   private
8579
     */
8580
    function debug($string)
8581
    {
8582
        $this->debug_str .= get_class($this) . ": $string\n";
8583
    }
8584
8585
    /**
8586
     * gets a wsdl instance from the cache
8587
     *
8588
     * @param string $wsdl The URL of the wsdl instance
8589
     * @return object wsdl The cached wsdl instance, null if the instance is not in the cache
8590
     * @access public
8591
     */
8592
    function get($wsdl)
8593
    {
8594
        $filename = $this->createFilename($wsdl);
8595
        if ($this->obtainMutex($filename, "r")) {
8596
            // check for expired WSDL that must be removed from the cache
8597
            if ($this->cache_lifetime > 0) {
8598
                if (file_exists($filename) && (time() - filemtime($filename) > $this->cache_lifetime)) {
8599
                    unlink($filename);
8600
                    $this->debug("Expired $wsdl ($filename) from cache");
8601
                    $this->releaseMutex($filename);
8602
                    return null;
8603
                }
8604
            }
8605
            // see what there is to return
8606
            if (!file_exists($filename)) {
8607
                $this->debug("$wsdl ($filename) not in cache (1)");
8608
                $this->releaseMutex($filename);
8609
                return null;
8610
            }
8611
            $fp = @fopen($filename, "r");
8612
            if ($fp) {
8613
                $s = implode("", @file($filename));
8614
                fclose($fp);
8615
                $this->debug("Got $wsdl ($filename) from cache");
8616
            } else {
8617
                $s = null;
8618
                $this->debug("$wsdl ($filename) not in cache (2)");
8619
            }
8620
            $this->releaseMutex($filename);
8621
            return (!is_null($s)) ? unserialize($s) : null;
8622
        } else {
8623
            $this->debug("Unable to obtain mutex for $filename in get");
8624
        }
8625
        return null;
8626
    }
8627
8628
    /**
8629
     * obtains the local mutex
8630
     *
8631
     * @param string $filename The Filename of the Cache to lock
8632
     * @param string $mode The open-mode ("r" or "w") or the file - affects lock-mode
8633
     * @return boolean Lock successfully obtained ?!
8634
     * @access private
8635
     */
8636
    function obtainMutex($filename, $mode)
8637
    {
8638
        if (isset($this->fplock[md5($filename)])) {
8639
            $this->debug("Lock for $filename already exists");
8640
            return false;
8641
        }
8642
        $this->fplock[md5($filename)] = fopen($filename . ".lock", "w");
8643
        if ($mode == "r") {
8644
            return flock($this->fplock[md5($filename)], LOCK_SH);
8645
        } else {
8646
            return flock($this->fplock[md5($filename)], LOCK_EX);
8647
        }
8648
    }
8649
8650
    /**
8651
     * adds a wsdl instance to the cache
8652
     *
8653
     * @param wsdl $wsdl_instance The wsdl instance to add
8654
     * @return boolean WSDL successfully cached
8655
     * @access public
8656
     */
8657
    function put($wsdl_instance)
8658
    {
8659
        $filename = $this->createFilename($wsdl_instance->wsdl);
8660
        $s = serialize($wsdl_instance);
8661
        if ($this->obtainMutex($filename, "w")) {
8662
            $fp = fopen($filename, "w");
8663
            if (! $fp) {
8664
                $this->debug("Cannot write $wsdl_instance->wsdl ($filename) in cache");
8665
                $this->releaseMutex($filename);
8666
                return false;
8667
            }
8668
            fputs($fp, $s);
8669
            fclose($fp);
8670
            $this->debug("Put $wsdl_instance->wsdl ($filename) in cache");
8671
            $this->releaseMutex($filename);
8672
            return true;
8673
        } else {
8674
            $this->debug("Unable to obtain mutex for $filename in put");
8675
        }
8676
        return false;
8677
    }
8678
8679
    /**
8680
     * releases the local mutex
8681
     *
8682
     * @param string $filename The Filename of the Cache to lock
8683
     * @return boolean Lock successfully released
8684
     * @access private
8685
     */
8686
    function releaseMutex($filename)
8687
    {
8688
        $ret = flock($this->fplock[md5($filename)], LOCK_UN);
8689
        fclose($this->fplock[md5($filename)]);
8690
        unset($this->fplock[md5($filename)]);
8691
        if (! $ret) {
8692
            $this->debug("Not able to release lock for $filename");
8693
        }
8694
        return $ret;
8695
    }
8696
8697
    /**
8698
     * removes a wsdl instance from the cache
8699
     *
8700
     * @param string $wsdl The URL of the wsdl instance
8701
     * @return boolean Whether there was an instance to remove
8702
     * @access public
8703
     */
8704
    function remove($wsdl)
8705
    {
8706
        $filename = $this->createFilename($wsdl);
8707
        if (!file_exists($filename)) {
8708
            $this->debug("$wsdl ($filename) not in cache to be removed");
8709
            return false;
8710
        }
8711
        // ignore errors obtaining mutex
8712
        $this->obtainMutex($filename, "w");
8713
        $ret = unlink($filename);
8714
        $this->debug("Removed ($ret) $wsdl ($filename) from cache");
8715
        $this->releaseMutex($filename);
8716
        return $ret;
8717
    }
8718
}
8719
8720
/**
8721
 * For backward compatibility
8722
 */
8723
class wsdlcache extends nusoap_wsdlcache
8724
{
8725
}
8726