Wsdl::webDescription()   D
last analyzed

Complexity

Conditions 9
Paths 14

Size

Total Lines 128
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 27
c 1
b 0
f 0
nc 14
nop 0
dl 0
loc 128
rs 4.8196

How to fix   Long Method   

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
//declare(strict_types=1);
4
5
/*
6
7
NuSOAP - Web Services Toolkit for PHP
8
9
Copyright (c) 2002 NuSphere Corporation
10
11
This library is free software; you can redistribute it and/or
12
modify it under the terms of the GNU Lesser General Public
13
License as published by the Free Software Foundation; either
14
version 2.1 of the License, or (at your option) any later version.
15
16
This library is distributed in the hope that it will be useful,
17
but WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
Lesser General Public License for more details.
20
21
You should have received a copy of the GNU Lesser General Public
22
License along with this library; if not, write to the Free Software
23
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
25
The NuSOAP project home is:
26
http://sourceforge.net/projects/nusoap/
27
28
The primary support for NuSOAP is the Help forum on the project home page.
29
30
If you have any questions or comments, please email:
31
32
Dietrich Ayala
33
[email protected]
34
http://dietrich.ganx4.com/nusoap
35
36
NuSphere Corporation
37
http://www.nusphere.com
38
39
*/
40
41
/*
42
 *	Some of the standards implmented in whole or part by NuSOAP:
43
 *
44
 *	SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
45
 *	WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
46
 *	SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
47
 *	XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
48
 *	Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
49
 *	XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
50
 *	RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
51
 *	RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
52
 *	RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
53
 */
54
55
/* load classes
56
57
// necessary classes
58
require_once('class.soapclient.php');
59
require_once('class.soap_val.php');
60
require_once('class.soap_parser.php');
61
require_once('class.soap_fault.php');
62
63
// transport classes
64
require_once('class.soap_transport_http.php');
65
66
// optional add-on classes
67
require_once('class.xmlschema.php');
68
require_once('class.wsdl.php');
69
70
// server class
71
require_once('class.soap_server.php');*/
72
73
// class variable emulation
74
// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
75
$GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
76
77
/**
78
 * Nusoap_base
79
 *
80
 * @author   Dietrich Ayala <[email protected]>
81
 * @author   Scott Nichol <[email protected]>
82
 */
83
class Nusoap_base
84
{
85
    /**
86
     * Identification for HTTP headers.
87
     *
88
     * @var string
89
     */
90
    protected $title = 'NuSOAP';
91
    /**
92
     * Version for HTTP headers.
93
     *
94
     * @var string
95
     */
96
    protected $version = '0.9.6';
97
    /**
98
     * CVS revision for HTTP headers.
99
     *
100
     * @var string
101
     */
102
    protected $revision = '$Revision: 1.123 $';
103
    /**
104
     * Current error string (manipulated by getError/setError)
105
     *
106
     * @var string
107
     */
108
    protected $error_str = '';
109
    /**
110
     * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
111
     *
112
     * @var string
113
     */
114
    private $debug_str = '';
115
    /**
116
     * Toggles automatic encoding of special characters as entities
117
     * (should always be true, I think)
118
     *
119
     * @var bool
120
     */
121
    private $charencoding = true;
122
    /**
123
     * The debug level for this instance
124
     *
125
     * @var    int
126
     */
127
    private $debugLevel;
128
    /**
129
     * Set schema version
130
     *
131
     * @var      string
132
     */
133
    public $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
134
    /**
135
     * Charset encoding for outgoing messages
136
     *
137
     * @var      string
138
     */
139
    public $soap_defencoding = 'ISO-8859-1';
140
    //var $soap_defencoding = 'UTF-8';
141
142
    /**
143
     * Namespaces in an array of prefix => uri
144
     *
145
     * This is "seeded" by a set of constants, but it may be altered by code
146
     *
147
     * @var      array
148
     */
149
    public $namespaces = [
150
        'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
151
        'xsd'      => 'http://www.w3.org/2001/XMLSchema',
152
        'xsi'      => 'http://www.w3.org/2001/XMLSchema-instance',
153
        'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
154
    ];
155
    /**
156
     * Namespaces used in the current context, e.g. during serialization
157
     *
158
     * @var      array
159
     */
160
    protected $usedNamespaces = [];
161
    /**
162
     * XML Schema types in an array of uri => (array of xml type => php type)
163
     * is this legacy yet?
164
     * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
165
     *
166
     * @var      array
167
     */
168
    public $typemap = [
169
        'http://www.w3.org/2001/XMLSchema'          => [
170
            'string'             => 'string',
171
            'boolean'            => 'boolean',
172
            'float'              => 'double',
173
            'double'             => 'double',
174
            'decimal'            => 'double',
175
            'duration'           => '',
176
            'dateTime'           => 'string',
177
            'time'               => 'string',
178
            'date'               => 'string',
179
            'gYearMonth'         => '',
180
            'gYear'              => '',
181
            'gMonthDay'          => '',
182
            'gDay'               => '',
183
            'gMonth'             => '',
184
            'hexBinary'          => 'string',
185
            'base64Binary'       => 'string',
186
            // abstract "any" types
187
            'anyType'            => 'string',
188
            'anySimpleType'      => 'string',
189
            // derived datatypes
190
            'normalizedString'   => 'string',
191
            'token'              => 'string',
192
            'language'           => '',
193
            'NMTOKEN'            => '',
194
            'NMTOKENS'           => '',
195
            'Name'               => '',
196
            'NCName'             => '',
197
            'ID'                 => '',
198
            'IDREF'              => '',
199
            'IDREFS'             => '',
200
            'ENTITY'             => '',
201
            'ENTITIES'           => '',
202
            'integer'            => 'integer',
203
            'nonPositiveInteger' => 'integer',
204
            'negativeInteger'    => 'integer',
205
            'long'               => 'integer',
206
            'int'                => 'integer',
207
            'short'              => 'integer',
208
            'byte'               => 'integer',
209
            'nonNegativeInteger' => 'integer',
210
            'unsignedLong'       => '',
211
            'unsignedInt'        => '',
212
            'unsignedShort'      => '',
213
            'unsignedByte'       => '',
214
            'positiveInteger'    => '',
215
        ],
216
        'http://www.w3.org/2000/10/XMLSchema'       => [
217
            'i4'           => '',
218
            'int'          => 'integer',
219
            'boolean'      => 'boolean',
220
            'string'       => 'string',
221
            'double'       => 'double',
222
            'float'        => 'double',
223
            'dateTime'     => 'string',
224
            'timeInstant'  => 'string',
225
            'base64Binary' => 'string',
226
            'base64'       => 'string',
227
            'ur-type'      => 'array',
228
        ],
229
        'http://www.w3.org/1999/XMLSchema'          => [
230
            'i4'           => '',
231
            'int'          => 'integer',
232
            'boolean'      => 'boolean',
233
            'string'       => 'string',
234
            'double'       => 'double',
235
            'float'        => 'double',
236
            'dateTime'     => 'string',
237
            'timeInstant'  => 'string',
238
            'base64Binary' => 'string',
239
            'base64'       => 'string',
240
            'ur-type'      => 'array',
241
        ],
242
        'http://soapinterop.org/xsd'                => ['SOAPStruct' => 'struct'],
243
        'http://schemas.xmlsoap.org/soap/encoding/' => ['base64' => 'string', 'array' => 'array', 'Array' => 'array'],
244
        'http://xml.apache.org/xml-soap'            => ['Map'],
245
    ];
246
    /**
247
     * XML entities to convert
248
     *
249
     * @var      array
250
     * @deprecated
251
     * @see      expandEntities
252
     */
253
    public $xmlEntities = [
254
        'quot' => '"',
255
        'amp'  => '&',
256
        'lt'   => '<',
257
        'gt'   => '>',
258
        'apos' => "'",
259
    ];
260
261
    /**
262
     * Constructor
263
     */
264
    public function __construct()
265
    {
266
        $this->debugLevel = $GLOBALS['_transient']['static']['Nusoap_base']['globalDebugLevel'];
267
    }
268
269
    /**
270
     * Gets the global debug level, which applies to future instances
271
     *
272
     * @return    int    Debug level 0-9, where 0 turns off
273
     */
274
    public function getGlobalDebugLevel()
275
    {
276
        return $GLOBALS['_transient']['static']['Nusoap_base']['globalDebugLevel'];
277
    }
278
279
    /**
280
     * Sets the global debug level, which applies to future instances
281
     *
282
     * @param    int $level Debug level 0-9, where 0 turns off
283
     */
284
    public function setGlobalDebugLevel($level)
285
    {
286
        $GLOBALS['_transient']['static']['Nusoap_base']['globalDebugLevel'] = $level;
287
    }
288
289
    /**
290
     * Gets the debug level for this instance
291
     *
292
     * @return    int    Debug level 0-9, where 0 turns off
293
     */
294
    public function getDebugLevel()
295
    {
296
        return $this->debugLevel;
297
    }
298
299
    /**
300
     * Sets the debug level for this instance
301
     *
302
     * @param    int $level Debug level 0-9, where 0 turns off
303
     */
304
    public function setDebugLevel($level)
305
    {
306
        $this->debugLevel = $level;
307
    }
308
309
    /**
310
     * Adds debug data to the instance debug string with formatting
311
     *
312
     * @param    string $string debug data
313
     */
314
    protected function debug($string)
315
    {
316
        if ($this->debugLevel > 0) {
317
            $this->appendDebug($this->getmicrotime() . ' ' . get_class($this) . ": $string\n");
318
        }
319
    }
320
321
    /**
322
     * Adds debug data to the instance debug string without formatting
323
     *
324
     * @param    string $string debug data
325
     */
326
    public function appendDebug($string)
327
    {
328
        if ($this->debugLevel > 0) {
329
            // it would be nice to use a memory stream here to use
330
            // memory more efficiently
331
            $this->debug_str .= $string;
332
        }
333
    }
334
335
    /**
336
     * Clears the current debug data for this instance
337
     */
338
    public function clearDebug()
339
    {
340
        // it would be nice to use a memory stream here to use
341
        // memory more efficiently
342
        $this->debug_str = '';
343
    }
344
345
    /**
346
     * Gets the current debug data for this instance
347
     *
348
     * @return   string debug data
349
     */
350
    public function &getDebug()
351
    {
352
        // it would be nice to use a memory stream here to use
353
        // memory more efficiently
354
        return $this->debug_str;
355
    }
356
357
    /**
358
     * Gets the current debug data for this instance as an XML comment
359
     * this may change the contents of the debug data
360
     *
361
     * @return   string debug data as an XML comment
362
     */
363
    public function &getDebugAsXMLComment()
364
    {
365
        // it would be nice to use a memory stream here to use
366
        // memory more efficiently
367
        while (mb_strpos($this->debug_str, '--')) {
368
            $this->debug_str = str_replace('--', '- -', $this->debug_str);
369
        }
370
        $ret = "<!--\n" . $this->debug_str . "\n-->";
371
372
        return $ret;
373
    }
374
375
    /**
376
     * Expands entities, e.g. changes '<' to '&lt;'.
377
     *
378
     * @param    string $val The string in which to expand entities.
379
     * @return mixed|string
380
     */
381
    protected function expandEntities($val)
382
    {
383
        if ($this->charencoding) {
384
            $val = str_replace('&', '&amp;', $val);
385
            $val = str_replace("'", '&apos;', $val);
386
            $val = str_replace('"', '&quot;', $val);
387
            $val = str_replace('<', '&lt;', $val);
388
            $val = str_replace('>', '&gt;', $val);
389
        }
390
391
        return $val;
392
    }
393
394
    /**
395
     * Returns error string if present
396
     *
397
     * @return   mixed error string or false
398
     */
399
    public function getError()
400
    {
401
        if ('' !== $this->error_str) {
402
            return $this->error_str;
403
        }
404
405
        return false;
406
    }
407
408
    /**
409
     * Sets error string
410
     *
411
     * @param string $str
412
     */
413
    protected function setError($str)
414
    {
415
        $this->error_str = $str;
416
    }
417
418
    /**
419
     * Detect if array is a simple array or a struct (associative array)
420
     *
421
     * @param    mixed $val The PHP array
422
     * @return    string    (arraySimple|arrayStruct)
423
     */
424
    protected function isArraySimpleOrStruct($val)
425
    {
426
        $keyList = array_keys($val);
427
        foreach ($keyList as $keyListValue) {
428
            if (!is_int($keyListValue)) {
429
                return 'arrayStruct';
430
            }
431
        }
432
433
        return 'arraySimple';
434
    }
435
436
    /**
437
     * Serializes PHP values in accordance w/ section 5. Type information is
438
     * not serialized if $use == 'literal'.
439
     *
440
     * @param    mixed          $val        The value to serialize
441
     * @param bool|string       $name       The name (local part) of the XML element
442
     * @param bool|string       $type       The XML schema type (local part) for the element
443
     * @param bool|string       $name_ns    The namespace for the name of the XML element
444
     * @param bool|string       $type_ns    The namespace for the type of the element
445
     * @param bool|string|array $attributes The attributes to serialize as name=>value pairs
446
     * @param    string         $use        The WSDL "use" (encoded|literal)
447
     * @param    bool           $soapval    Whether this is called from Soapval.
448
     * @return    string    The serialized element, possibly with child elements
449
     */
450
    public function serialize_val($val, $name = false, $type = false, $name_ns = false, $type_ns = false, $attributes = false, $use = 'encoded', $soapval = false)
451
    {
452
        $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, Soapval=$soapval");
453
        $this->appendDebug('value=' . $this->varDump($val));
454
        $this->appendDebug('attributes=' . $this->varDump($attributes));
455
        if (is_object($val) && 'soapval' === get_class($val) && (!$soapval)) {
456
            $this->debug('serialize_val: serialize Soapval');
457
            $xml = $val->serialize($use);
458
            $this->appendDebug($val->getDebug());
459
            $val->clearDebug();
460
            $this->debug("serialize_val of Soapval returning $xml");
461
462
            return $xml;
463
        }
464
        // force valid name if necessary
465
        if (is_numeric($name)) {
466
            $name = '__numeric_' . $name;
467
        } elseif (!$name) {
468
            $name = 'noname';
469
        }
470
        // if name has ns, add ns prefix to name
471
        $xmlns = '';
472
        if ($name_ns) {
473
            $prefix = 'nu' . mt_rand(1000, 9999);
474
            $name   = $prefix . ':' . $name;
0 ignored issues
show
Bug introduced by
Are you sure $name of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

474
            $name   = $prefix . ':' . /** @scrutinizer ignore-type */ $name;
Loading history...
475
            $xmlns  .= " xmlns:$prefix=\"$name_ns\"";
476
        }
477
        // if type is prefixed, create type prefix
478
        if ('' !== $type_ns && $type_ns === $this->namespaces['xsd']) {
479
            // need to fix this. shouldn't default to xsd if no ns specified
480
            // w/o checking against typemap
481
            $type_prefix = 'xsd';
482
        } elseif ($type_ns) {
483
            $type_prefix = 'ns' . mt_rand(1000, 9999);
484
            $xmlns       .= " xmlns:$type_prefix=\"$type_ns\"";
485
        }
486
        // serialize attributes if present
487
        $atts = '';
488
        if ($attributes) {
489
            foreach ($attributes as $k => $v) {
490
                $atts .= " $k=\"" . $this->expandEntities($v) . '"';
491
            }
492
        }
493
        // serialize null value
494
        if (null === $val) {
495
            $this->debug('serialize_val: serialize null');
496
            if ('literal' === $use) {
497
                // TODO: depends on minOccurs
498
                $xml = "<$name$xmlns$atts>";
499
                $this->debug("serialize_val returning $xml");
500
501
                return $xml;
502
            }
503
                if (isset($type) && isset($type_prefix)) {
504
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
505
                } else {
506
                    $type_str = '';
507
                }
508
                $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\">";
509
                $this->debug("serialize_val returning $xml");
510
511
                return $xml;
512
            }
513
        // serialize if an xsd built-in primitive type
514
        if ('' !== $type && isset($this->typemap[$this->XMLSchemaVersion][$type])) {
515
            $this->debug('serialize_val: serialize xsd built-in primitive type');
516
            if (is_bool($val)) {
517
                if ('boolean' === $type) {
518
                    $val = $val ? 'true' : 'false';
519
                } elseif (!$val) {
520
                    $val = 0;
521
                }
522
            } elseif (is_string($val)) {
523
                $val = $this->expandEntities($val);
524
            }
525
            if ('literal' === $use) {
526
                $xml = "<$name$xmlns$atts>$val</$name>";
527
                $this->debug("serialize_val returning $xml");
528
529
                return $xml;
530
            }
531
                $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
532
                $this->debug("serialize_val returning $xml");
533
534
                return $xml;
535
            }
536
        // detect type and serialize
537
        $xml = '';
538
        switch (true) {
539
            case (is_bool($val) || 'boolean' === $type):
540
                $this->debug('serialize_val: serialize boolean');
541
                if ('boolean' === $type) {
542
                    $val = $val ? 'true' : 'false';
543
                } elseif (!$val) {
544
                    $val = 0;
545
                }
546
                if ('literal' === $use) {
547
                    $xml .= "<$name$xmlns$atts>$val</$name>";
548
                } else {
549
                    $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
550
                }
551
552
                break;
553
            case (is_int($val) || is_int($val) || 'int' === $type):
554
                $this->debug('serialize_val: serialize int');
555
                if ('literal' === $use) {
556
                    $xml .= "<$name$xmlns$atts>$val</$name>";
557
                } else {
558
                    $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
559
                }
560
561
                break;
562
            case (is_float($val) || is_float($val) || 'float' === $type):
563
                $this->debug('serialize_val: serialize float');
564
                if ('literal' === $use) {
565
                    $xml .= "<$name$xmlns$atts>$val</$name>";
566
                } else {
567
                    $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
568
                }
569
570
                break;
571
            case (is_string($val) || 'string' === $type):
572
                $this->debug('serialize_val: serialize string');
573
                $val = $this->expandEntities($val);
574
                if ('literal' === $use) {
575
                    $xml .= "<$name$xmlns$atts>$val</$name>";
576
                } else {
577
                    $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
578
                }
579
580
                break;
581
            case is_object($val):
582
                $this->debug('serialize_val: serialize object');
583
                if ('soapval' === get_class($val)) {
584
                    $this->debug('serialize_val: serialize Soapval object');
585
                    $pXml = $val->serialize($use);
586
                    $this->appendDebug($val->getDebug());
587
                    $val->clearDebug();
588
                } else {
589
                    if (!$name) {
590
                        $name = get_class($val);
591
                        $this->debug("In serialize_val, used class name $name as element name");
592
                    } else {
593
                        $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
594
                    }
595
                    foreach (get_object_vars($val) as $k => $v) {
596
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
597
                    }
598
                }
599
                $type_str = '';
600
                if (isset($type) && isset($type_prefix)) {
601
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
602
                }
603
                if ('literal' === $use) {
604
                    $xml .= "<$name$xmlns$atts>$pXml</$name>";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pXml does not seem to be defined for all execution paths leading up to this point.
Loading history...
605
                } else {
606
                    $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
607
                }
608
609
                break;
610
            case (is_array($val) || $type):
611
                // detect if struct or array
612
                $array_types = [];
613
                $tt_ns = $tt = '';
614
                $valueType = $this->isArraySimpleOrStruct($val);
615
                if ('arraySimple' === $valueType || preg_match('/^ArrayOf/', $type)) {
616
                    $this->debug('serialize_val: serialize array');
617
                    $i = 0;
618
                    if (is_array($val) && count($val) > 0) {
619
                        foreach ($val as $v) {
620
                            if (is_object($v) && 'soapval' === get_class($v)) {
621
                                $tt_ns = $v->type_ns;
622
                                $tt    = $v->type;
623
                            } elseif (is_array($v)) {
624
                                $tt = $this->isArraySimpleOrStruct($v);
625
                            } else {
626
                                $tt = gettype($v);
627
                            }
628
                            $array_types[$tt] = 1;
629
                            // TODO: for literal, the name should be $name
630
                            $xml .= $this->serialize_val($v, 'item', false, false, false, false, $use);
631
                            ++$i;
632
                        }
633
                        if (is_array($array_types) && count($array_types) > 1) {
634
                            $array_typename = 'xsd:anyType';
635
                        } elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
636
                            if ('integer' === $tt) {
637
                                $tt = 'int';
638
                            }
639
                            $array_typename = 'xsd:' . $tt;
640
                        } elseif (isset($tt) && 'arraySimple' === $tt) {
641
                            $array_typename = 'SOAP-ENC:Array';
642
                        } elseif (isset($tt) && 'arrayStruct' === $tt) {
643
                            $array_typename = 'unnamed_struct_use_soapval';
644
                        } else {
645
                            // if type is prefixed, create type prefix
646
                            if ('' !== $tt_ns && $tt_ns === $this->namespaces['xsd']) {
647
                                $array_typename = 'xsd:' . $tt;
648
                            } elseif ($tt_ns) {
649
                                $tt_prefix      = 'ns' . mt_rand(1000, 9999);
650
                                $array_typename = "$tt_prefix:$tt";
651
                                $xmlns          .= " xmlns:$tt_prefix=\"$tt_ns\"";
652
                            } else {
653
                                $array_typename = $tt;
654
                            }
655
                        }
656
                        $array_type = $i;
657
                        if ('literal' === $use) {
658
                            $type_str = '';
659
                        } elseif (isset($type) && isset($type_prefix)) {
660
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
661
                        } else {
662
                            $type_str = ' xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="' . $array_typename . "[$array_type]\"";
663
                        }
664
                        // empty array
665
                    } else {
666
                        $type_str = ' xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:anyType[0]"';
667
                        if ('literal' === $use) {
668
                            $type_str = '';
669
                        } elseif (isset($type) && isset($type_prefix)) {
670
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
671
                        }
672
                    }
673
                    // TODO: for array in literal, there is no wrapper here
674
                    $xml = "<$name$xmlns$type_str$atts>" . $xml . "</$name>";
675
                } else {
676
                    // got a struct
677
                    $this->debug('serialize_val: serialize struct');
678
                    $type_str = '';
679
                    if (isset($type) && isset($type_prefix)) {
680
                        $type_str = " xsi:type=\"$type_prefix:$type\"";
681
                    }
682
                    if ('literal' === $use) {
683
                        $xml .= "<$name$xmlns$atts>";
684
                    } else {
685
                        $xml .= "<$name$xmlns$type_str$atts>";
686
                    }
687
                    foreach ($val as $k => $v) {
688
                        // Apache Map
689
                        if ('Map' === $type && 'http://xml.apache.org/xml-soap' === $type_ns) {
690
                            $xml .= '<item>';
691
                            $xml .= $this->serialize_val($k, 'key', false, false, false, false, $use);
692
                            $xml .= $this->serialize_val($v, 'value', false, false, false, false, $use);
693
                            $xml .= '</item>';
694
                        } else {
695
                            $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
696
                        }
697
                    }
698
                    $xml .= "</$name>";
699
                }
700
701
                break;
702
            default:
703
                $this->debug('serialize_val: serialize unknown');
704
                $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
705
706
                break;
707
        }
708
        $this->debug("serialize_val returning $xml");
709
710
        return $xml;
711
    }
712
713
    /**
714
     * Serializes a message
715
     *
716
     * @param string $body          the XML of the SOAP body
717
     * @param mixed  $headers       optional string of XML with SOAP header content, or array of Soapval objects for SOAP headers, or associative array
718
     * @param array  $namespaces    optional the namespaces used in generating the body and headers
719
     * @param string $style         optional (rpc|document)
720
     * @param string $use           optional (encoded|literal)
721
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
722
     * @return string the message
723
     */
724
    public function serializeEnvelope($body, $headers = false, $namespaces = [], $style = 'rpc', $use = 'encoded', $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/')
725
    {
726
        // TODO: add an option to automatically run utf8_encode on $body and $headers
727
        // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
728
        // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
729
730
        $this->debug('In serializeEnvelope length=' . mb_strlen($body) . ' body (max 1000 characters)=' . mb_substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
731
        $this->debug('headers:');
732
        $this->appendDebug($this->varDump($headers));
733
        $this->debug('namespaces:');
734
        $this->appendDebug($this->varDump($namespaces));
735
        // serialize namespaces
736
        $ns_string = '';
737
        foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
738
            $ns_string .= " xmlns:$k=\"$v\"";
739
        }
740
        if ($encodingStyle) {
741
            $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
742
        }
743
744
        // serialize headers
745
        if ($headers) {
746
            if (is_array($headers)) {
747
                $xml = '';
748
                foreach ($headers as $k => $v) {
749
                    if (is_object($v) && 'soapval' === get_class($v)) {
750
                        $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
751
                    } else {
752
                        $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
753
                    }
754
                }
755
                $headers = $xml;
756
                $this->debug("In serializeEnvelope, serialized array of headers to $headers");
757
            }
758
            $headers = '<SOAP-ENV:Header>' . $headers . '</SOAP-ENV:Header>';
759
        }
760
        // serialize envelope
761
        return '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . '>' . '<SOAP-ENV:Envelope' . $ns_string . '>' . $headers . '<SOAP-ENV:Body>' . $body . '</SOAP-ENV:Body>' . '</SOAP-ENV:Envelope>';
0 ignored issues
show
Bug introduced by
Are you sure $headers of type false|string|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

761
        return '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . '>' . '<SOAP-ENV:Envelope' . $ns_string . '>' . /** @scrutinizer ignore-type */ $headers . '<SOAP-ENV:Body>' . $body . '</SOAP-ENV:Body>' . '</SOAP-ENV:Envelope>';
Loading history...
762
    }
763
764
    /**
765
     * Formats a string to be inserted into an HTML stream
766
     *
767
     * @param string $str The string to format
768
     * @return string The formatted string
769
     * @deprecated
770
     */
771
    public function formatDump($str)
772
    {
773
        $str = htmlspecialchars($str);
774
775
        return nl2br($str);
776
    }
777
778
    /**
779
     * Contracts (changes namespace to prefix) a qualified name
780
     *
781
     * @param    string $qname qname
782
     * @return    string contracted qname
783
     */
784
    protected function contractQName($qname)
785
    {
786
        // get element namespace
787
        //$this->xdebug("Contract $qname");
788
        if (mb_strrpos($qname, ':')) {
789
            // get unqualified name
790
            $name = mb_substr($qname, mb_strrpos($qname, ':') + 1);
791
            // get ns
792
            $ns = mb_substr($qname, 0, mb_strrpos($qname, ':'));
793
            $p  = $this->getPrefixFromNamespace($ns);
794
            if ($p) {
795
                return $p . ':' . $name;
796
            }
797
798
            return $qname;
799
        }
800
801
        return $qname;
802
    }
803
804
    /**
805
     * Expands (changes prefix to namespace) a qualified name
806
     *
807
     * @param    string $qname qname
808
     * @return    string expanded qname
809
     */
810
    protected function expandQname($qname)
811
    {
812
        // get element prefix
813
        if (mb_strpos($qname, ':') && !preg_match('/^http:\/\//', $qname)) {
814
            // get unqualified name
815
            $name = mb_substr(mb_strstr($qname, ':'), 1);
816
            // get ns prefix
817
            $prefix = mb_substr($qname, 0, mb_strpos($qname, ':'));
818
            if (isset($this->namespaces[$prefix])) {
819
                return $this->namespaces[$prefix] . ':' . $name;
820
            }
821
822
                return $qname;
823
            }
824
825
            return $qname;
826
        }
827
828
    /**
829
     * Returns the local part of a prefixed string
830
     * Returns the original string, if not prefixed
831
     *
832
     * @param string $str The prefixed string
833
     * @return string The local part
834
     */
835
    public function getLocalPart($str)
836
    {
837
        if (false !== ($sstr = mb_strrchr($str, ':'))) {
838
            // get unqualified name
839
            return mb_substr($sstr, 1);
840
        }
841
842
            return $str;
843
        }
844
845
    /**
846
     * Returns the prefix part of a prefixed string
847
     * Returns false, if not prefixed
848
     *
849
     * @param string $str The prefixed string
850
     * @return mixed The prefix or false if there is no prefix
851
     */
852
    public function getPrefix($str)
853
    {
854
        if (false !== ($pos = mb_strrpos($str, ':'))) {
855
            // get prefix
856
            return mb_substr($str, 0, $pos);
857
        }
858
859
        return false;
860
    }
861
862
    /**
863
     * Pass it a prefix, it returns a namespace
864
     *
865
     * @param string $prefix The prefix
866
     * @return mixed The namespace, false if no namespace has the specified prefix
867
     */
868
    public function getNamespaceFromPrefix($prefix)
869
    {
870
        if (isset($this->namespaces[$prefix])) {
871
            return $this->namespaces[$prefix];
872
        }
873
        //$this->setError("No namespace registered for prefix '$prefix'");
874
        return false;
875
    }
876
877
    /**
878
     * Returns the prefix for a given namespace (or prefix)
879
     * or false if no prefixes registered for the given namespace
880
     *
881
     * @param string $ns The namespace
882
     * @return mixed The prefix, false if the namespace has no prefixes
883
     */
884
    public function getPrefixFromNamespace($ns)
885
    {
886
        foreach ($this->namespaces as $p => $n) {
887
            if ($ns === $n || $ns === $p) {
888
                $this->usedNamespaces[$p] = $n;
889
890
                return $p;
891
            }
892
        }
893
894
        return false;
895
    }
896
897
    /**
898
     * Returns the time in ODBC canonical form with microseconds
899
     *
900
     * @return string The time in ODBC canonical form with microseconds
901
     */
902
    public function getmicrotime()
903
    {
904
        if (function_exists('gettimeofday')) {
905
            $tod  = gettimeofday();
906
            $sec  = $tod['sec'];
907
            $usec = $tod['usec'];
908
        } else {
909
            $sec  = time();
910
            $usec = 0;
911
        }
912
913
        return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
914
    }
915
916
    /**
917
     * Returns a string with the output of var_dump
918
     *
919
     * @param mixed $data The variable to var_dump
920
     * @return string The output of var_dump
921
     */
922
    public function varDump($data)
923
    {
924
        ob_start();
925
        var_dump($data);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($data) looks like debug code. Are you sure you do not want to remove it?
Loading history...
926
        $ret_val = ob_get_contents();
927
        ob_end_clean();
928
929
        return $ret_val;
930
    }
931
932
    /**
933
     * Represents the object as a string
934
     *
935
     * @return    string
936
     */
937
    public function __toString()
938
    {
939
        return $this->varDump($this);
940
    }
941
}
942
943
// XML Schema Datatype Helper Functions
944
945
//xsd:dateTime helpers
946
947
/**
948
 * Convert unix timestamp to ISO 8601 compliant date string
949
 *
950
 * @param    int  $timestamp Unix time stamp
951
 * @param    bool $utc       Whether the time stamp is UTC or local
952
 * @return    mixed ISO 8601 date string or false
953
 */
954
function timestamp_to_iso8601($timestamp, $utc = true)
955
{
956
    $datestr = date('Y-m-d\TH:i:sO', $timestamp);
957
    $pos     = mb_strrpos($datestr, '+');
958
    if (false === $pos) {
959
        $pos = mb_strrpos($datestr, '-');
960
    }
961
    if (false !== $pos) {
962
        if (mb_strlen($datestr) === $pos + 5) {
963
            $datestr = mb_substr($datestr, 0, $pos + 3) . ':' . mb_substr($datestr, -2);
964
        }
965
    }
966
    if ($utc) {
967
        $pattern = '/' . '([0-9]{4})-' .    // centuries & years CCYY-
968
                   '([0-9]{2})-' .    // months MM-
969
                   '([0-9]{2})' .    // days DD
970
                   'T' .            // separator T
971
                   '([0-9]{2}):' .    // hours hh:
972
                   '([0-9]{2}):' .    // minutes mm:
973
                   '([0-9]{2})(\.[0-9]*)?' . // seconds ss.ss...
974
                   '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
975
                   '/';
976
        if (preg_match($pattern, $datestr, $regs)) {
977
            return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
978
        }
979
980
        return false;
981
    }
982
983
        return $datestr;
984
    }
985
986
/**
987
 * Convert ISO 8601 compliant date string to unix timestamp
988
 *
989
 * @param    string $datestr ISO 8601 compliant date string
990
 * @return    mixed Unix timestamp (int) or false
991
 */
992
function iso8601_to_timestamp($datestr)
993
{
994
    $pattern = '/' . '([0-9]{4})-' .    // centuries & years CCYY-
995
               '([0-9]{2})-' .    // months MM-
996
               '([0-9]{2})' .    // days DD
997
               'T' .            // separator T
998
               '([0-9]{2}):' .    // hours hh:
999
               '([0-9]{2}):' .    // minutes mm:
1000
               '([0-9]{2})(\.[0-9]+)?' . // seconds ss.ss...
1001
               '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
1002
               '/';
1003
    if (preg_match($pattern, $datestr, $regs)) {
1004
        // not utc
1005
        if ('Z' !== $regs[8]) {
1006
            $op = mb_substr($regs[8], 0, 1);
1007
            $h  = mb_substr($regs[8], 1, 2);
1008
            $m  = mb_substr($regs[8], mb_strlen($regs[8]) - 2, 2);
1009
            if ('-' === $op) {
1010
                $regs[4] += $h;
1011
                $regs[5] += $m;
1012
            } elseif ('+' === $op) {
1013
                $regs[4] -= $h;
1014
                $regs[5] -= $m;
1015
            }
1016
        }
1017
1018
        return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1019
    //		return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
1020
    }
1021
1022
        return false;
1023
    }
1024
1025
/**
1026
 * Sleeps some number of microseconds
1027
 *
1028
 * @param    string $usec the number of microseconds to sleep
1029
 * @deprecated
1030
 */
1031
function usleepWindows($usec)
1032
{
1033
    $start = gettimeofday();
1034
    do {
1035
        $stop       = gettimeofday();
1036
        $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + $stop['usec'] - $start['usec'];
1037
    } while ($timePassed < $usec);
1038
}
1039
1040
/**
1041
 * Contains information for a SOAP fault.
1042
 * Mainly used for returning faults from deployed functions
1043
 * in a server instance.
1044
 * @author   Dietrich Ayala <[email protected]>
1045
 */
1046
class Nusoap_fault extends Nusoap_base
1047
{
1048
    /**
1049
     * The fault code (client|server)
1050
     * @var string
1051
     */
1052
    private $faultcode;
1053
    /**
1054
     * The fault actor
1055
     * @var string
1056
     */
1057
    private $faultactor;
1058
    /**
1059
     * The fault string, a description of the fault
1060
     * @var string
1061
     */
1062
    private $faultstring;
1063
    /**
1064
     * The fault detail, typically a string or array of string
1065
     * @var mixed
1066
     */
1067
    private $faultdetail;
1068
1069
    /**
1070
     * Constructor
1071
     *
1072
     * @param string $faultcode   (SOAP-ENV:Client | SOAP-ENV:Server)
1073
     * @param string $faultactor  only used when msg routed between multiple actors
1074
     * @param string $faultstring human readable error message
1075
     * @param mixed  $faultdetail detail, typically a string or array of string
1076
     */
1077
    public function __construct($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '')
1078
    {
1079
        parent::__construct();
1080
        $this->faultcode   = $faultcode;
1081
        $this->faultactor  = $faultactor;
1082
        $this->faultstring = $faultstring;
1083
        $this->faultdetail = $faultdetail;
1084
    }
1085
1086
    /**
1087
     * Serialize a fault
1088
     *
1089
     * @return    string    The serialization of the fault instance.
1090
     */
1091
    public function serialize()
1092
    {
1093
        $ns_string = '';
1094
        foreach ($this->namespaces as $k => $v) {
1095
            $ns_string .= "\n  xmlns:$k=\"$v\"";
1096
        }
1097
        $return_msg = '<?xml version="1.0" encoding="'
1098
                      . $this->soap_defencoding
1099
                      . '"?>'
1100
                      . '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'
1101
                      . $ns_string
1102
                      . ">\n"
1103
                      . '<SOAP-ENV:Body>'
1104
                      . '<SOAP-ENV:Fault>'
1105
                      . $this->serialize_val($this->faultcode, 'faultcode')
1106
                      . $this->serialize_val($this->faultactor, 'faultactor')
1107
                      . $this->serialize_val($this->faultstring, 'faultstring')
1108
                      . $this->serialize_val($this->faultdetail, 'detail')
1109
                      . '</SOAP-ENV:Fault>'
1110
                      . '</SOAP-ENV:Body>'
1111
                      . '</SOAP-ENV:Envelope>';
1112
1113
        return $return_msg;
1114
    }
1115
}
1116
1117
/**
1118
 * Backward compatibility
1119
 */
1120
class Soap_fault extends Nusoap_fault
1121
{
1122
}
1123
1124
/**
1125
 * Parses an XML Schema, allows access to it's data, other utility methods.
1126
 * imperfect, no validation... yet, but quite functional.
1127
 *
1128
 * @author   Dietrich Ayala <[email protected]>
1129
 * @author   Scott Nichol <[email protected]>
1130
 */
1131
class Nusoap_xmlschema extends Nusoap_base
1132
{
1133
    // files
1134
    public $schema = '';
1135
    public $xml    = '';
1136
    // namespaces
1137
    public $enclosingNamespaces;
1138
    // schema info
1139
    public $schemaInfo            = [];
1140
    public $schemaTargetNamespace = '';
1141
    // types, elements, attributes defined by the schema
1142
    public $attributes       = [];
1143
    public $complexTypes     = [];
1144
    public $complexTypeStack = [];
1145
    public $currentComplexType;
1146
    public $elements         = [];
1147
    public $elementStack     = [];
1148
    public $currentElement;
1149
    public $simpleTypes      = [];
1150
    public $simpleTypeStack  = [];
1151
    public $currentSimpleType;
1152
    // imports
1153
    public $imports = [];
1154
    // parser vars
1155
    public $parser;
1156
    public $position         = 0;
1157
    public $depth            = 0;
1158
    public $depth_array      = [];
1159
    public $message          = [];
1160
    public $defaultNamespace = [];
1161
1162
    /**
1163
     * Constructor
1164
     *
1165
     * @param string $schema     schema document URI
1166
     * @param string $xml        xml document URI
1167
     * @param array  $namespaces namespaces defined in enclosing XML
1168
     */
1169
    public function __construct($schema = '', $xml = '', $namespaces = [])
1170
    {
1171
        parent::__construct();
1172
        $this->debug('Nusoap_xmlschema class instantiated, inside constructor');
1173
        // files
1174
        $this->schema = $schema;
1175
        $this->xml    = $xml;
1176
        // namespaces
1177
        $this->enclosingNamespaces = $namespaces;
1178
        $this->namespaces          = array_merge($this->namespaces, $namespaces);
1179
        // parse schema file
1180
        if ('' !== $schema) {
1181
            $this->debug('initial schema file: ' . $schema);
1182
            $this->parseFile($schema, 'schema');
1183
        }
1184
1185
        // parse xml file
1186
        if ('' !== $xml) {
1187
            $this->debug('initial xml file: ' . $xml);
1188
            $this->parseFile($xml, 'xml');
1189
        }
1190
    }
1191
1192
    /**
1193
     * Parse an XML file
1194
     *
1195
     * @param string $xml  path/URL to XML file
1196
     * @param string $type (schema | xml)
1197
     * @return bool
1198
     */
1199
    public function parseFile($xml, $type)
1200
    {
1201
        // parse xml file
1202
        if ('' !== $xml) {
1203
            $xmlStr = @file_get_contents($xml);
1204
            if ('' === $xmlStr) {
1205
                $msg = 'Error reading XML from ' . $xml;
1206
                $this->setError($msg);
1207
                $this->debug($msg);
1208
1209
                return false;
1210
            }
1211
1212
            $this->debug("parsing $xml");
1213
            $this->parseString($xmlStr, $type);
1214
            $this->debug("done parsing $xml");
1215
1216
            return true;
1217
        }
1218
1219
        return false;
1220
    }
1221
1222
    /**
1223
     * Parse an XML string
1224
     *
1225
     * @param    string $xml  path or URL
1226
     * @param    string $type (schema|xml)
1227
     */
1228
    private function parseString($xml, $type)
1229
    {
1230
        // parse xml string
1231
        if ('' !== $xml) {
1232
            // Create an XML parser.
1233
            $this->parser = xml_parser_create();
1234
            // Set the options for parsing the XML data.
1235
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1236
            // Set the object for the parser.
1237
            xml_set_object($this->parser, $this);
1238
            // Set the element handlers for the parser.
1239
            if ('schema' === $type) {
1240
                xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
1241
                xml_set_character_data_handler($this->parser, 'schemaCharacterData');
1242
            } elseif ('xml' === $type) {
1243
                xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
1244
                xml_set_character_data_handler($this->parser, 'xmlCharacterData');
1245
            }
1246
1247
            // Parse the XML file.
1248
            if (!xml_parse($this->parser, $xml, true)) {
1249
                // Display an error message.
1250
                $errstr = sprintf('XML error parsing XML schema on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)));
1251
                $this->debug($errstr);
1252
                $this->debug("XML payload:\n" . $xml);
1253
                $this->setError($errstr);
1254
            }
1255
1256
            xml_parser_free($this->parser);
1257
            unset($this->parser);
1258
        } else {
1259
            $this->debug('no xml passed to parseString()!!');
1260
            $this->setError('no xml passed to parseString()!!');
1261
        }
1262
    }
1263
1264
    /**
1265
     * Gets a type name for an unnamed type
1266
     * @param    string  $ename  Element name
1267
     * @return    string    A type name for an unnamed type
1268
     */
1269
    private function CreateTypeName($ename)
1270
    {
1271
        $scope = '';
1272
        for ($i = 0, $iMax = count($this->complexTypeStack); $i < $iMax; ++$i) {
1273
            $scope .= $this->complexTypeStack[$i] . '_';
1274
        }
1275
1276
        return $scope . $ename . '_ContainedType';
1277
    }
1278
1279
    /**
1280
     * Start-element handler
1281
     *
1282
     * @param    string       $parser XML parser object
1283
     * @param    string       $name   element name
1284
     * @param    string|array $attrs  associative array of attributes
1285
     */
1286
    public function schemaStartElement($parser, $name, $attrs)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1286
    public function schemaStartElement(/** @scrutinizer ignore-unused */ $parser, $name, $attrs)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1287
    {
1288
        // position in the total number of elements, starting from 0
1289
        $eAttrs = [];
1290
        $aname = '';
1291
        $pos   = $this->position++;
1292
        $depth = $this->depth++;
1293
        // set self as current value for this depth
1294
        $this->depth_array[$depth] = $pos;
1295
        $this->message[$pos]       = ['cdata' => ''];
1296
        if ($depth > 0) {
1297
            $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1298
        } else {
1299
            $this->defaultNamespace[$pos] = false;
1300
        }
1301
1302
        // get element prefix
1303
        if (false !== ($prefix = $this->getPrefix($name))) {
1304
            // get unqualified name
1305
            $name = $this->getLocalPart($name);
1306
        } else {
1307
            $prefix = '';
1308
        }
1309
1310
        // loop thru attributes, expanding, and registering namespace declarations
1311
        if (is_array($attrs) && count($attrs) > 0) {
1312
            foreach ($attrs as $k => $v) {
1313
                // if ns declarations, add to class level array of valid namespaces
1314
                if (preg_match('/^xmlns/', $k)) {
1315
                    //$this->xdebug("$k: $v");
1316
                    //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1317
                    if (false !== ($ns_prefix = mb_substr(mb_strrchr($k, ':'), 1))) {
1318
                        //$this->xdebug("Add namespace[$ns_prefix] = $v");
1319
                        $this->namespaces[$ns_prefix] = $v;
1320
                    } else {
1321
                        $this->defaultNamespace[$pos] = $v;
1322
                        if (!$this->getPrefixFromNamespace($v)) {
1323
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
1324
                        }
1325
                    }
1326
                    if ('http://www.w3.org/2001/XMLSchema' === $v || 'http://www.w3.org/1999/XMLSchema' === $v || 'http://www.w3.org/2000/10/XMLSchema' === $v) {
1327
                        $this->XMLSchemaVersion  = $v;
1328
                        $this->namespaces['xsi'] = $v . '-instance';
1329
                    }
1330
                }
1331
            }
1332
            foreach ($attrs as $k => $v) {
1333
                // expand each attribute
1334
                $k          = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
1335
                $v          = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
1336
                $eAttrs[$k] = $v;
1337
            }
1338
            $attrs = $eAttrs;
1339
        } else {
1340
            $attrs = [];
1341
        }
1342
        // find status, register data
1343
        switch ($name) {
1344
            case 'all':
1345
           // (optional) compositor content for a complexType
1346
            case 'choice':
1347
            case 'group':
1348
            case 'sequence':
1349
                //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1350
1351
                $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1352
                //if($name == 'all' || $name == 'sequence'){
1353
                //	$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1354
                //}
1355
1356
                break;
1357
            case 'attribute':
1358
            // complexType attribute
1359
                //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1360
                $this->xdebug('parsing attribute:');
1361
                $this->appendDebug($this->varDump($attrs));
1362
                if (!isset($attrs['form'])) {
1363
                    // TODO: handle globals
1364
                    $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1365
                }
1366
                if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1367
                    $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1368
                    if (!mb_strpos($v, ':')) {
1369
                        // no namespace in arrayType attribute value...
1370
                        if ($this->defaultNamespace[$pos]) {
1371
                            // ...so use the default
1372
                            $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1373
                        }
1374
                    }
1375
                }
1376
                if (isset($attrs['name'])) {
1377
                    $this->attributes[$attrs['name']] = $attrs;
1378
                    $aname                            = $attrs['name'];
1379
                } elseif (isset($attrs['ref']) && 'http://schemas.xmlsoap.org/soap/encoding/:arrayType' === $attrs['ref']) {
1380
                    $aname = '';
1381
                    if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1382
                        $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1383
                    }
1384
                } elseif (isset($attrs['ref'])) {
1385
                    $aname                           = $attrs['ref'];
1386
                    $this->attributes[$attrs['ref']] = $attrs;
1387
                }
1388
1389
                if ($this->currentComplexType) {
1390
                    // This should *always* be
1391
                    $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1392
                }
1393
                // arrayType attribute
1394
                if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || 'arrayType' === $this->getLocalPart($aname)) {
1395
                    $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1396
                    $prefix                                                   = $this->getPrefix($aname);
0 ignored issues
show
Unused Code introduced by
The assignment to $prefix is dead and can be removed.
Loading history...
1397
                    $v                                                        = '';
1398
                    if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1399
                        $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1400
                    }
1401
                    if (mb_strpos($v, '[,]')) {
1402
                        $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1403
                    }
1404
                    $v = mb_substr($v, 0, mb_strpos($v, '[')); // clip the []
1405
                    if (!mb_strpos($v, ':') && isset($this->typemap[$this->XMLSchemaVersion][$v])) {
1406
                        $v = $this->XMLSchemaVersion . ':' . $v;
1407
                    }
1408
                    $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1409
                }
1410
1411
                break;
1412
            case 'complexContent':
1413
            // (optional) content for a complexType
1414
                $this->xdebug("do nothing for element $name");
1415
1416
                break;
1417
            case 'complexType':
1418
                $this->complexTypeStack[] = $this->currentComplexType;
1419
                if (isset($attrs['name'])) {
1420
                    // TODO: what is the scope of named complexTypes that appear
1421
                    //       nested within other c complexTypes?
1422
                    $this->xdebug('processing named complexType ' . $attrs['name']);
1423
                    //$this->currentElement = false;
1424
                    $this->currentComplexType                                   = $attrs['name'];
1425
                    $this->complexTypes[$this->currentComplexType]              = $attrs;
1426
                    $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1427
                    // This is for constructs like
1428
                    //           <complexType name="ListOfString" base="soap:Array">
1429
                    //                <sequence>
1430
                    //                    <element name="string" type="xsd:string"
1431
                    //                        minOccurs="0" maxOccurs="unbounded" >
1432
                    //                </sequence>
1433
                    //            </complexType>
1434
                    if (isset($attrs['base']) && preg_match('/:Array$/', $attrs['base'])) {
1435
                        $this->xdebug('complexType is unusual array');
1436
                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1437
                    } else {
1438
                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1439
                    }
1440
                } else {
1441
                    $name = $this->CreateTypeName($this->currentElement);
1442
                    $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1443
                    $this->currentComplexType = $name;
1444
                    //$this->currentElement = false;
1445
                    $this->complexTypes[$this->currentComplexType]              = $attrs;
1446
                    $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1447
                    // This is for constructs like
1448
                    //           <complexType name="ListOfString" base="soap:Array">
1449
                    //                <sequence>
1450
                    //                    <element name="string" type="xsd:string"
1451
                    //                        minOccurs="0" maxOccurs="unbounded" >
1452
                    //                </sequence>
1453
                    //            </complexType>
1454
                    if (isset($attrs['base']) && preg_match('/:Array$/', $attrs['base'])) {
1455
                        $this->xdebug('complexType is unusual array');
1456
                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1457
                    } else {
1458
                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1459
                    }
1460
                }
1461
                $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
1462
1463
                break;
1464
            case 'element':
1465
                $this->elementStack[] = $this->currentElement;
1466
                if (!isset($attrs['form'])) {
1467
                    if ($this->currentComplexType) {
1468
                        $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1469
                    } else {
1470
                        // global
1471
                        $attrs['form'] = 'qualified';
1472
                    }
1473
                }
1474
                if (isset($attrs['type'])) {
1475
                    $this->xdebug('processing typed element ' . $attrs['name'] . ' of type ' . $attrs['type']);
1476
                    if (!$this->getPrefix($attrs['type'])) {
1477
                        if ($this->defaultNamespace[$pos]) {
1478
                            $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1479
                            $this->xdebug('used default namespace to make type ' . $attrs['type']);
1480
                        }
1481
                    }
1482
                    // This is for constructs like
1483
                    //           <complexType name="ListOfString" base="soap:Array">
1484
                    //                <sequence>
1485
                    //                    <element name="string" type="xsd:string"
1486
                    //                        minOccurs="0" maxOccurs="unbounded" >
1487
                    //                </sequence>
1488
                    //            </complexType>
1489
                    if ($this->currentComplexType && 'array' === $this->complexTypes[$this->currentComplexType]['phpType']) {
1490
                        $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1491
                        $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1492
                    }
1493
                    $this->currentElement = $attrs['name'];
1494
                    $ename                = $attrs['name'];
1495
                } elseif (isset($attrs['ref'])) {
1496
                    $this->xdebug('processing element as ref to ' . $attrs['ref']);
1497
                    $this->currentElement = 'ref to ' . $attrs['ref'];
1498
                    $ename                = $this->getLocalPart($attrs['ref']);
1499
                } else {
1500
                    $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1501
                    $this->xdebug('processing untyped element ' . $attrs['name'] . ' type ' . $type);
1502
                    $this->currentElement = $attrs['name'];
1503
                    $attrs['type']        = $this->schemaTargetNamespace . ':' . $type;
1504
                    $ename                = $attrs['name'];
1505
                }
1506
                if (isset($ename) && $this->currentComplexType) {
1507
                    $this->xdebug("add element $ename to complexType $this->currentComplexType");
1508
                    $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1509
                } elseif (!isset($attrs['ref'])) {
1510
                    $this->xdebug("add element $ename to elements array");
1511
                    $this->elements[$attrs['name']]              = $attrs;
1512
                    $this->elements[$attrs['name']]['typeClass'] = 'element';
1513
                }
1514
1515
                break;
1516
            case 'enumeration':
1517
            //	restriction value list member
1518
                $this->xdebug('enumeration ' . $attrs['value']);
1519
                if ($this->currentSimpleType) {
1520
                    $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1521
                } elseif ($this->currentComplexType) {
1522
                    $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1523
                }
1524
1525
                break;
1526
            case 'extension':
1527
            // simpleContent or complexContent type extension
1528
                $this->xdebug('extension ' . $attrs['base']);
1529
                if ($this->currentComplexType) {
1530
                    $ns = $this->getPrefix($attrs['base']);
1531
                    if ('' === $ns) {
1532
                        $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
1533
                    } else {
1534
                        $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1535
                    }
1536
                } else {
1537
                    $this->xdebug('no current complexType to set extensionBase');
1538
                }
1539
1540
                break;
1541
            case 'import':
1542
                if (isset($attrs['schemaLocation'])) {
1543
                    $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1544
                    $this->imports[$attrs['namespace']][] = ['location' => $attrs['schemaLocation'], 'loaded' => false];
1545
                } else {
1546
                    $this->xdebug('import namespace ' . $attrs['namespace']);
1547
                    $this->imports[$attrs['namespace']][] = ['location' => '', 'loaded' => true];
1548
                    if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
1549
                        $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
1550
                    }
1551
                }
1552
1553
                break;
1554
            case 'include':
1555
                if (isset($attrs['schemaLocation'])) {
1556
                    $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
1557
                    $this->imports[$this->schemaTargetNamespace][] = ['location' => $attrs['schemaLocation'], 'loaded' => false];
1558
                } else {
1559
                    $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
1560
                }
1561
1562
                break;
1563
            case 'list':
1564
            // simpleType value list
1565
                $this->xdebug("do nothing for element $name");
1566
1567
                break;
1568
            case 'restriction':
1569
            // simpleType, simpleContent or complexContent value restriction
1570
                $this->xdebug('restriction ' . $attrs['base']);
1571
                if ($this->currentSimpleType) {
1572
                    $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1573
                } elseif ($this->currentComplexType) {
1574
                    $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1575
                    if (':Array' === mb_strstr($attrs['base'], ':')) {
1576
                        $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1577
                    }
1578
                }
1579
1580
                break;
1581
            case 'schema':
1582
                $this->schemaInfo                  = $attrs;
1583
                $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1584
                if (isset($attrs['targetNamespace'])) {
1585
                    $this->schemaTargetNamespace = $attrs['targetNamespace'];
1586
                }
1587
                if (!isset($attrs['elementFormDefault'])) {
1588
                    $this->schemaInfo['elementFormDefault'] = 'unqualified';
1589
                }
1590
                if (!isset($attrs['attributeFormDefault'])) {
1591
                    $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1592
                }
1593
1594
                break;
1595
            case 'simpleContent':
1596
            // (optional) content for a complexType
1597
                if ($this->currentComplexType) {
1598
                    // This should *always* be
1599
                    $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
1600
                } else {
1601
                    $this->xdebug("do nothing for element $name because there is no current complexType");
1602
                }
1603
1604
                break;
1605
            case 'simpleType':
1606
                $this->simpleTypeStack[] = $this->currentSimpleType;
1607
                if (isset($attrs['name'])) {
1608
                    $this->xdebug('processing simpleType for name ' . $attrs['name']);
1609
                    $this->currentSimpleType                        = $attrs['name'];
1610
                    $this->simpleTypes[$attrs['name']]              = $attrs;
1611
                    $this->simpleTypes[$attrs['name']]['typeClass'] = 'simpleType';
1612
                    $this->simpleTypes[$attrs['name']]['phpType']   = 'scalar';
1613
                } else {
1614
                    $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1615
                    $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1616
                    $this->currentSimpleType = $name;
1617
                    //$this->currentElement = false;
1618
                    $this->simpleTypes[$this->currentSimpleType]            = $attrs;
1619
                    $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1620
                }
1621
1622
                break;
1623
            case 'union':
1624
            // simpleType type list
1625
                $this->xdebug("do nothing for element $name");
1626
1627
                break;
1628
            default:
1629
                $this->xdebug("do not have any logic to process element $name");
1630
        }
1631
    }
1632
1633
    /**
1634
     * End-element handler
1635
     *
1636
     * @param    string $parser XML parser object
1637
     * @param    string $name   element name
1638
     */
1639
    private function schemaEndElement($parser, $name)
0 ignored issues
show
Unused Code introduced by
The method schemaEndElement() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1639
    private function schemaEndElement(/** @scrutinizer ignore-unused */ $parser, $name)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1640
    {
1641
        // bring depth down a notch
1642
        $this->depth--;
1643
        // position of current element is equal to the last value left in depth_array for my depth
1644
        if (isset($this->depth_array[$this->depth])) {
1645
            $pos = $this->depth_array[$this->depth];
0 ignored issues
show
Unused Code introduced by
The assignment to $pos is dead and can be removed.
Loading history...
1646
        }
1647
        // get element prefix
1648
        if (false !== ($prefix = $this->getPrefix($name))) {
0 ignored issues
show
Unused Code introduced by
The assignment to $prefix is dead and can be removed.
Loading history...
1649
            // get unqualified name
1650
            $name = $this->getLocalPart($name);
1651
        } else {
1652
            $prefix = '';
1653
        }
1654
        // move on...
1655
        if ('complexType' === $name) {
1656
            $this->xdebug('done processing complexType ' . ($this->currentComplexType ?: '(unknown)'));
1657
            $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1658
            $this->currentComplexType = array_pop($this->complexTypeStack);
1659
            //$this->currentElement = false;
1660
        }
1661
        if ('element' === $name) {
1662
            $this->xdebug('done processing element ' . ($this->currentElement ?: '(unknown)'));
1663
            $this->currentElement = array_pop($this->elementStack);
1664
        }
1665
        if ('simpleType' === $name) {
1666
            $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ?: '(unknown)'));
1667
            $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1668
            $this->currentSimpleType = array_pop($this->simpleTypeStack);
1669
        }
1670
    }
1671
1672
    /**
1673
     * Element content handler
1674
     *
1675
     * @param    string $parser XML parser object
1676
     * @param    string $data   element content
1677
     */
1678
    private function schemaCharacterData($parser, $data)
0 ignored issues
show
Unused Code introduced by
The method schemaCharacterData() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1678
    private function schemaCharacterData(/** @scrutinizer ignore-unused */ $parser, $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1679
    {
1680
        $pos                          = $this->depth_array[$this->depth - 1];
1681
        $this->message[$pos]['cdata'] .= $data;
1682
    }
1683
1684
    /**
1685
     * Serialize the schema
1686
     */
1687
    public function serializeSchema()
1688
    {
1689
        $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1690
        $xml          = '';
1691
        // imports
1692
        if (is_array($this->imports) && count($this->imports) > 0) {
1693
            foreach ($this->imports as $ns => $list) {
1694
                foreach ($list as $ii) {
1695
                    if ('' !== $ii['location']) {
1696
                        $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" >\n";
1697
                    } else {
1698
                        $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" >\n";
1699
                    }
1700
                }
1701
            }
1702
        }
1703
        // complex types
1704
        foreach ($this->complexTypes as $typeName => $attrs) {
1705
            $contentStr = '';
1706
            // serialize child elements
1707
            if (isset($attrs['elements']) && (count($attrs['elements']) > 0)) {
1708
                foreach ($attrs['elements'] as $element => $eParts) {
1709
                    if (isset($eParts['ref'])) {
1710
                        $contentStr .= "   <$schemaPrefix:element ref=\"$element\">\n";
1711
                    } else {
1712
                        $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . '"';
1713
                        foreach ($eParts as $aName => $aValue) {
1714
                            // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1715
                            if ('name' !== $aName && 'type' !== $aName) {
1716
                                $contentStr .= " $aName=\"$aValue\"";
1717
                            }
1718
                        }
1719
                        $contentStr .= ">\n";
1720
                    }
1721
                }
1722
                // compositor wraps elements
1723
                if (isset($attrs['compositor']) && ('' !== $attrs['compositor'])) {
1724
                    $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n" . $contentStr . "  </$schemaPrefix:$attrs[compositor]>\n";
1725
                }
1726
            }
1727
            // attributes
1728
            if (isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)) {
1729
                foreach ($attrs['attrs'] as $attr => $aParts) {
1730
                    $contentStr .= "    <$schemaPrefix:attribute";
1731
                    foreach ($aParts as $a => $v) {
1732
                        if ('ref' === $a || 'type' === $a) {
1733
                            $contentStr .= " $a=\"" . $this->contractQName($v) . '"';
1734
                        } elseif ('http://schemas.xmlsoap.org/wsdl/:arrayType' === $a) {
1735
                            $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1736
                            $contentStr .= ' Wsdl:arrayType="' . $this->contractQName($v) . '"';
1737
                        } else {
1738
                            $contentStr .= " $a=\"$v\"";
1739
                        }
1740
                    }
1741
                    $contentStr .= ">\n";
1742
                }
1743
            }
1744
            // if restriction
1745
            if (isset($attrs['restrictionBase']) && '' !== $attrs['restrictionBase']) {
1746
                $contentStr = "   <$schemaPrefix:restriction base=\"" . $this->contractQName($attrs['restrictionBase']) . "\">\n" . $contentStr . "   </$schemaPrefix:restriction>\n";
1747
                // complex or simple content
1748
                if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)) {
1749
                    $contentStr = "  <$schemaPrefix:complexContent>\n" . $contentStr . "  </$schemaPrefix:complexContent>\n";
1750
                }
1751
            }
1752
            // finalize complex type
1753
            if ('' !== $contentStr) {
1754
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n" . $contentStr . " </$schemaPrefix:complexType>\n";
1755
            } else {
1756
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n";
1757
            }
1758
            $xml .= $contentStr;
1759
        }
1760
        // simple types
1761
        if (isset($this->simpleTypes) && count($this->simpleTypes) > 0) {
1762
            foreach ($this->simpleTypes as $typeName => $eParts) {
1763
                $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"" . $this->contractQName($eParts['type']) . "\">\n";
1764
                if (isset($eParts['enumeration'])) {
1765
                    foreach ($eParts['enumeration'] as $e) {
1766
                        $xml .= "  <$schemaPrefix:enumeration value=\"$e\">\n";
1767
                    }
1768
                }
1769
                $xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1770
            }
1771
        }
1772
        // elements
1773
        if (isset($this->elements) && count($this->elements) > 0) {
1774
            foreach ($this->elements as $element => $eParts) {
1775
                $xml .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\">\n";
1776
            }
1777
        }
1778
        // attributes
1779
        if (isset($this->attributes) && count($this->attributes) > 0) {
1780
            foreach ($this->attributes as $attr => $aParts) {
1781
                $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"" . $this->contractQName($aParts['type']) . "\"\n>";
1782
            }
1783
        }
1784
        // finish 'er up
1785
        $attr = '';
1786
        foreach ($this->schemaInfo as $k => $v) {
1787
            if ('elementFormDefault' === $k || 'attributeFormDefault' === $k) {
1788
                $attr .= " $k=\"$v\"";
1789
            }
1790
        }
1791
        $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1792
        foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1793
            $el .= " xmlns:$nsp=\"$ns\"";
1794
        }
1795
        $xml = $el . ">\n" . $xml . "</$schemaPrefix:schema>\n";
1796
1797
        return $xml;
1798
    }
1799
1800
    /**
1801
     * Adds debug data to the clas level debug string
1802
     *
1803
     * @param    string $string debug data
1804
     */
1805
    private function xdebug($string)
1806
    {
1807
        $this->debug('<' . $this->schemaTargetNamespace . '> ' . $string);
1808
    }
1809
1810
    /**
1811
     * Get the PHP type of a user defined type in the schema
1812
     * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1813
     * Returns false if no type exists, or not w/ the given namespace
1814
     * Else returns a string that is either a native php type, or 'struct'
1815
     *
1816
     * @param string $type name of defined type
1817
     * @param string $ns   namespace of type
1818
     * @return mixed
1819
     * @deprecated
1820
     */
1821
    public function getPHPType($type, $ns)
1822
    {
1823
        if (isset($this->typemap[$ns][$type])) {
1824
            //print "found type '$type' and ns $ns in typemap<br>";
1825
            return $this->typemap[$ns][$type];
1826
        }
1827
1828
        if (isset($this->complexTypes[$type])) {
1829
            //print "getting type '$type' and ns $ns from complexTypes array<br>";
1830
            return $this->complexTypes[$type]['phpType'];
1831
        }
1832
1833
        return false;
1834
    }
1835
1836
    /**
1837
     * Returns an associative array of information about a given type
1838
     * Returns false if no type exists by the given name
1839
     *
1840
     *    For a complexType typeDef = array(
1841
     *    'restrictionBase' => '',
1842
     *    'phpType' => '',
1843
     *    'compositor' => '(sequence|all)',
1844
     *    'elements' => array(), // refs to elements array
1845
     *    'attrs' => array() // refs to attributes array
1846
     *    ... and so on (see addComplexType)
1847
     *    )
1848
     *
1849
     *   For simpleType or element, the array has different keys.
1850
     *
1851
     * @param string $type
1852
     * @return mixed
1853
     * @see    addComplexType
1854
     * @see    addSimpleType
1855
     * @see    addElement
1856
     */
1857
    public function getTypeDef($type)
1858
    {
1859
        $typeDef = [];
1860
        //$this->debug("in getTypeDef for type $type");
1861
        if ('^' === mb_substr($type, -1)) {
1862
            $is_element = 1;
1863
            $type       = mb_substr($type, 0, -1);
1864
        } else {
1865
            $is_element = 0;
1866
        }
1867
1868
        if ((!$is_element) && isset($this->complexTypes[$type])) {
1869
            $this->xdebug("in getTypeDef, found complexType $type");
1870
1871
            return $this->complexTypes[$type];
1872
        }
1873
1874
        if ((!$is_element) && isset($this->simpleTypes[$type])) {
1875
            $this->xdebug("in getTypeDef, found simpleType $type");
1876
            if (!isset($this->simpleTypes[$type]['phpType'])) {
1877
                // get info for type to tack onto the simple type
1878
                // TODO: can this ever really apply (i.e. what is a simpleType really?)
1879
                $uqType = mb_substr($this->simpleTypes[$type]['type'], mb_strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1880
                $ns     = mb_substr($this->simpleTypes[$type]['type'], 0, mb_strrpos($this->simpleTypes[$type]['type'], ':'));
0 ignored issues
show
Unused Code introduced by
The assignment to $ns is dead and can be removed.
Loading history...
1881
                $etype  = $this->getTypeDef($uqType);
1882
                if ($etype) {
1883
                    $this->xdebug("in getTypeDef, found type for simpleType $type:");
1884
                    $this->xdebug($this->varDump($etype));
1885
                    if (isset($etype['phpType'])) {
1886
                        $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1887
                    }
1888
                    if (isset($etype['elements'])) {
1889
                        $this->simpleTypes[$type]['elements'] = $etype['elements'];
1890
                    }
1891
                }
1892
            }
1893
1894
            return $this->simpleTypes[$type];
1895
        } elseif (isset($this->elements[$type])) {
1896
            $this->xdebug("in getTypeDef, found element $type");
1897
            if (!isset($this->elements[$type]['phpType'])) {
1898
                // get info for type to tack onto the element
1899
                $uqType = mb_substr($this->elements[$type]['type'], mb_strrpos($this->elements[$type]['type'], ':') + 1);
1900
                $ns     = mb_substr($this->elements[$type]['type'], 0, mb_strrpos($this->elements[$type]['type'], ':'));
1901
                $etype  = $this->getTypeDef($uqType);
1902
                if ($etype) {
1903
                    $this->xdebug("in getTypeDef, found type for element $type:");
1904
                    $this->xdebug($this->varDump($etype));
1905
                    if (isset($etype['phpType'])) {
1906
                        $this->elements[$type]['phpType'] = $etype['phpType'];
1907
                    }
1908
                    if (isset($etype['elements'])) {
1909
                        $this->elements[$type]['elements'] = $etype['elements'];
1910
                    }
1911
                    if (isset($etype['extensionBase'])) {
1912
                        $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
1913
                    }
1914
                } elseif ('http://www.w3.org/2001/XMLSchema' === $ns) {
1915
                    $this->xdebug("in getTypeDef, element $type is an XSD type");
1916
                    $this->elements[$type]['phpType'] = 'scalar';
1917
                }
1918
            }
1919
1920
            return $this->elements[$type];
1921
        } elseif (isset($this->attributes[$type])) {
1922
            $this->xdebug("in getTypeDef, found attribute $type");
1923
1924
            return $this->attributes[$type];
1925
        } elseif (preg_match('/_ContainedType$/', $type)) {
1926
            $this->xdebug("in getTypeDef, have an untyped element $type");
1927
            $typeDef['typeClass'] = 'simpleType';
1928
            $typeDef['phpType']   = 'scalar';
1929
            $typeDef['type']      = 'http://www.w3.org/2001/XMLSchema:string';
1930
1931
            return $typeDef;
1932
        }
1933
        $this->xdebug("in getTypeDef, did not find $type");
1934
1935
        return false;
1936
    }
1937
1938
    /**
1939
     * Returns a sample serialization of a given type, or false if no type by the given name
1940
     *
1941
     * @param string $type name of type
1942
     * @return mixed
1943
     * @deprecated
1944
     */
1945
    public function serializeTypeDef($type)
1946
    {
1947
        $str = '';
1948
        //print "in sTD() for type $type<br>";
1949
        if (false !== ($typeDef = $this->getTypeDef($type))) {
1950
            $str .= '<' . $type;
1951
            if (is_array($typeDef['attrs'])) {
1952
                foreach ($typeDef['attrs'] as $attName => $data) {
1953
                    $str .= " $attName=\"{type = " . $data['type'] . '}"';
1954
                }
1955
            }
1956
            $str .= ' xmlns="' . $this->schema['targetNamespace'] . '"';
1957
            if (is_array($typeDef['elements']) && count($typeDef['elements']) > 0) {
1958
                $str .= '>';
1959
                foreach ($typeDef['elements'] as $element => $eData) {
1960
                    $str .= $this->serializeTypeDef($element);
0 ignored issues
show
Deprecated Code introduced by
The function Nusoap_xmlschema::serializeTypeDef() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1960
                    $str .= /** @scrutinizer ignore-deprecated */ $this->serializeTypeDef($element);
Loading history...
1961
                }
1962
                $str .= "</$type>";
1963
            } elseif ('element' === $typeDef['typeClass']) {
1964
                $str .= "></$type>";
1965
            } else {
1966
                $str .= '>';
1967
            }
1968
1969
            return $str;
1970
        }
1971
1972
        return false;
1973
    }
1974
1975
    /**
1976
     * Returns HTML form elements that allow a user
1977
     * to enter values for creating an instance of the given type.
1978
     *
1979
     * @param string $name name for type instance
1980
     * @param string $type name of type
1981
     * @return string
1982
     * @deprecated
1983
     */
1984
    public function typeToForm($name, $type)
1985
    {
1986
        $buffer = '';
1987
        // get typedef
1988
        if (false !== ($typeDef = $this->getTypeDef($type))) {
1989
            // if struct
1990
            if ('struct' === $typeDef['phpType']) {
1991
                $buffer .= '<table>';
1992
                foreach ($typeDef['elements'] as $child => $childDef) {
1993
                    $buffer .= "
1994
					<tr><td align='right'>$childDef[name] (type: " . $this->getLocalPart($childDef['type']) . "):</td>
1995
					<td><input type='text' name='parameters[" . $name . "][$childDef[name]]'></td></tr>";
1996
                }
1997
                $buffer .= '</table>';
1998
            // if array
1999
            } elseif ('array' === $typeDef['phpType']) {
2000
                $buffer .= '<table>';
2001
                for ($i = 0; $i < 3; ++$i) {
2002
                    $buffer .= "
2003
					<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
2004
					<td><input type='text' name='parameters[" . $name . "][]'></td></tr>";
2005
                }
2006
                $buffer .= '</table>';
2007
            // if scalar
2008
            } else {
2009
                $buffer .= "<input type='text' name='parameters[$name]'>";
2010
            }
2011
        } else {
2012
            $buffer .= "<input type='text' name='parameters[$name]'>";
2013
        }
2014
2015
        return $buffer;
2016
    }
2017
2018
    /**
2019
     * Adds a complex type to the schema
2020
     *
2021
     * Example: array
2022
     *
2023
     * addType(
2024
     *    'ArrayOfstring',
2025
     *    'complexType',
2026
     *    'array',
2027
     *    '',
2028
     *    'SOAP-ENC:Array',
2029
     *    array('ref'=>'SOAP-ENC:arrayType','Wsdl:arrayType'=>'string[]'),
2030
     *    'xsd:string'
2031
     * );
2032
     *
2033
     * Example: PHP associative array ( SOAP Struct )
2034
     *
2035
     * addType(
2036
     *    'SOAPStruct',
2037
     *    'complexType',
2038
     *    'struct',
2039
     *    'all',
2040
     *    array('myVar'=> array('name'=>'myVar','type'=>'string')
2041
     * );
2042
     *
2043
     * @param string $name
2044
     * @param string $typeClass (complexType|simpleType|attribute)
2045
     * @param string $phpType : currently supported are array and struct (php assoc array)
2046
     * @param string $compositor      (all|sequence|choice)
2047
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2048
     * @param array  $elements        = array ( name = array(name=>'',type=>'') )
2049
     * @param array  $attrs           = array(
2050
     *                                array(
2051
     *                                'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
2052
     *                                "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
2053
     *                                )
2054
     *                                )
2055
     * @param string $arrayType : namespace:name (http://www.w3.org/2001/XMLSchema:string)
2056
     */
2057
    public function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = [], $attrs = [], $arrayType = '')
2058
    {
2059
        $this->complexTypes[$name] = [
2060
            'name'            => $name,
2061
            'typeClass'       => $typeClass,
2062
            'phpType'         => $phpType,
2063
            'compositor'      => $compositor,
2064
            'restrictionBase' => $restrictionBase,
2065
            'elements'        => $elements,
2066
            'attrs'           => $attrs,
2067
            'arrayType'       => $arrayType,
2068
        ];
2069
        $this->xdebug("addComplexType $name:");
2070
        $this->appendDebug($this->varDump($this->complexTypes[$name]));
2071
    }
2072
2073
    /**
2074
     * Adds a simple type to the schema
2075
     *
2076
     * @param string $name
2077
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2078
     * @param string $typeClass       (should always be simpleType)
2079
     * @param string $phpType         (should always be scalar)
2080
     * @param array  $enumeration     array of values
2081
     * @see    nusoap_xmlschema
2082
     * @see    getTypeDef
2083
     */
2084
    public function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = [])
2085
    {
2086
        $this->simpleTypes[$name] = [
2087
            'name'        => $name,
2088
            'typeClass'   => $typeClass,
2089
            'phpType'     => $phpType,
2090
            'type'        => $restrictionBase,
2091
            'enumeration' => $enumeration,
2092
        ];
2093
        $this->xdebug("addSimpleType $name:");
2094
        $this->appendDebug($this->varDump($this->simpleTypes[$name]));
2095
    }
2096
2097
    /**
2098
     * Adds an element to the schema
2099
     *
2100
     * @param array $attrs attributes that must include name and type
2101
     * @see    nusoap_xmlschema
2102
     */
2103
    public function addElement($attrs)
2104
    {
2105
        if (!$this->getPrefix($attrs['type'])) {
2106
            $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
2107
        }
2108
        $this->elements[$attrs['name']]              = $attrs;
2109
        $this->elements[$attrs['name']]['typeClass'] = 'element';
2110
        $this->xdebug('addElement ' . $attrs['name']);
2111
        $this->appendDebug($this->varDump($this->elements[$attrs['name']]));
2112
    }
2113
}
2114
2115
/**
2116
 * Backward compatibility
2117
 */
2118
class XMLSchema extends Nusoap_xmlschema
2119
{
2120
}
2121
2122
/**
2123
 * For creating serializable abstractions of native PHP types.  This class
2124
 * allows element name/namespace, XSD type, and XML attributes to be
2125
 * associated with a value.  This is extremely useful when WSDL is not
2126
 * used, but is also useful when WSDL is used with polymorphic types, including
2127
 * xsd:anyType and user-defined types.
2128
 *
2129
 * @author   Dietrich Ayala <[email protected]>
2130
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2131
 */
2132
class Soapval extends Nusoap_base
2133
{
2134
    /**
2135
     * The XML element name
2136
     *
2137
     * @var string
2138
     */
2139
    private $name;
2140
    /**
2141
     * The XML type name (string or false)
2142
     *
2143
     * @var mixed
2144
     */
2145
    public $type;
2146
    /**
2147
     * The PHP value
2148
     *
2149
     * @var mixed
2150
     */
2151
    public $value;
2152
    /**
2153
     * The XML element namespace (string or false)
2154
     *
2155
     * @var mixed
2156
     */
2157
    private $element_ns;
2158
    /**
2159
     * The XML type namespace (string or false)
2160
     *
2161
     * @var mixed
2162
     */
2163
    public $type_ns;
2164
    /**
2165
     * The XML element attributes (array or false)
2166
     *
2167
     * @var mixed
2168
     */
2169
    public $attributes;
2170
2171
    /**
2172
     * Constructor
2173
     *
2174
     * @param    string $name       optional name
2175
     * @param    mixed  $type       optional type name
2176
     * @param    mixed  $value      optional value
2177
     * @param    mixed  $element_ns optional namespace of value
2178
     * @param    mixed  $type_ns    optional namespace of type
2179
     * @param    mixed  $attributes associative array of attributes to add to element serialization
2180
     */
2181
    public function __construct($name = 'soapval', $type = false, $value = -1, $element_ns = false, $type_ns = false, $attributes = false)
2182
    {
2183
        parent::__construct();
2184
        $this->name       = $name;
2185
        $this->type       = $type;
2186
        $this->value      = $value;
2187
        $this->element_ns = $element_ns;
2188
        $this->type_ns    = $type_ns;
2189
        $this->attributes = $attributes;
2190
    }
2191
2192
    /**
2193
     * Return serialized value
2194
     *
2195
     * @param    string $use The WSDL use value (encoded|literal)
2196
     * @return    string XML data
2197
     */
2198
    public function serialize($use = 'encoded')
2199
    {
2200
        return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2201
    }
2202
2203
    /**
2204
     * Decodes a Soapval object into a PHP native type
2205
     *
2206
     * @return    mixed
2207
     */
2208
    public function decode()
2209
    {
2210
        return $this->value;
2211
    }
2212
}
2213
2214
/**
2215
 * Transport class for sending/receiving data via HTTP and HTTPS
2216
 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2217
 *
2218
 * @author   Dietrich Ayala <[email protected]>
2219
 * @author   Scott Nichol <[email protected]>
2220
 */
2221
class Soap_transport_http extends Nusoap_base
2222
{
2223
    public $url                  = '';
2224
    public $uri                  = '';
2225
    public $digest_uri           = '';
2226
    public $scheme               = '';
2227
    public $host                 = '';
2228
    public $port                 = 0;
2229
    public $path                 = '';
2230
    public $request_method       = 'POST';
2231
    public $protocol_version     = '1.0';
2232
    public $encoding             = '';
2233
    public $outgoing_headers     = [];
2234
    public $incoming_headers     = [];
2235
    public $incoming_cookies     = [];
2236
    public $outgoing_payload     = '';
2237
    public $incoming_payload     = '';
2238
    public $response_status_line;    // HTTP response status line
2239
    public $useSOAPAction        = true;
2240
    public $persistentConnection = false;
2241
    public $ch                   = false;    // cURL handle
2242
    public $ch_options           = [];    // cURL custom options
2243
    public $use_curl             = false;        // force cURL use
2244
    public $proxy;            // proxy information (associative array)
2245
    public $username             = '';
2246
    public $password             = '';
2247
    public $authtype             = '';
2248
    public $digestRequest        = [];
2249
    public $certRequest          = [];
2250
    public $tryagain;
2251
    // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2252
    // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2253
    // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2254
    // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2255
    // passphrase: SSL key password/passphrase
2256
    // certpassword: SSL certificate password
2257
    // verifypeer: default is 1
2258
    // verifyhost: default is 1
2259
2260
    /**
2261
     * Constructor
2262
     *
2263
     * @param string $url          The URL to which to connect
2264
     * @param array  $curl_options User-specified cURL options
2265
     * @param bool   $use_curl     Whether to try to force cURL use
2266
     */
2267
    public function __construct($url, $curl_options = null, $use_curl = false)
2268
    {
2269
        parent::__construct();
2270
        $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2271
        $this->appendDebug($this->varDump($curl_options));
2272
        $this->setURL($url);
2273
        if (is_array($curl_options)) {
2274
            $this->ch_options = $curl_options;
2275
        }
2276
        $this->use_curl = $use_curl;
2277
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
2278
        $this->setHeader('User-Agent', $this->title . '/' . $this->version . ' (' . $rev[1] . ')');
2279
    }
2280
2281
    /**
2282
     * Sets a cURL option
2283
     *
2284
     * @param    mixed $option The cURL option (always integer?)
2285
     * @param    mixed $value  The cURL option value
2286
     */
2287
    private function setCurlOption($option, $value)
2288
    {
2289
        $this->debug("setCurlOption option=$option, value=");
2290
        $this->appendDebug($this->varDump($value));
2291
        curl_setopt($this->ch, $option, $value);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type resource expected by parameter $ch of curl_setopt(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2291
        curl_setopt(/** @scrutinizer ignore-type */ $this->ch, $option, $value);
Loading history...
2292
    }
2293
2294
    /**
2295
     * Sets an HTTP header
2296
     *
2297
     * @param string $name  The name of the header
2298
     * @param string $value The value of the header
2299
     */
2300
    private function setHeader($name, $value)
2301
    {
2302
        $this->outgoing_headers[$name] = $value;
2303
        $this->debug("set header $name: $value");
2304
    }
2305
2306
    /**
2307
     * Unsets an HTTP header
2308
     *
2309
     * @param string $name The name of the header
2310
     */
2311
    private function unsetHeader($name)
2312
    {
2313
        if (isset($this->outgoing_headers[$name])) {
2314
            $this->debug("unset header $name");
2315
            unset($this->outgoing_headers[$name]);
2316
        }
2317
    }
2318
2319
    /**
2320
     * Sets the URL to which to connect
2321
     *
2322
     * @param string $url The URL to which to connect
2323
     */
2324
    private function setURL($url)
2325
    {
2326
        $this->url = $url;
2327
        $u         = parse_url($url);
2328
        foreach ($u as $k => $v) {
2329
            $this->debug("parsed URL $k = $v");
2330
            $this->$k = $v;
2331
        }
2332
2333
        // add any GET params to path
2334
        if (isset($u['query']) && '' !== $u['query']) {
2335
            $this->path .= '?' . $u['query'];
2336
        }
2337
2338
        // set default port
2339
        if (!isset($u['port'])) {
2340
            $this->port = 80;
2341
            if ('https' === $u['scheme']) {
2342
                $this->port = 443;
2343
            }
2344
        }
2345
2346
        $this->uri        = $this->path;
2347
        $this->digest_uri = $this->uri;
2348
        // build headers
2349
        if (!isset($u['port'])) {
2350
            $this->setHeader('Host', $this->host);
2351
        } else {
2352
            $this->setHeader('Host', $this->host . ':' . $this->port);
2353
        }
2354
2355
        if (isset($u['user']) && '' !== $u['user']) {
2356
            $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2357
        }
2358
    }
2359
2360
    /**
2361
     * Gets the I/O method to use
2362
     *
2363
     * @return    string    I/O method to use (socket|curl|unknown)
2364
     */
2365
    private function io_method()
2366
    {
2367
        if ($this->use_curl || ('https' === $this->scheme) || ('http' === $this->scheme && 'ntlm' === $this->authtype) || ('http' === $this->scheme && is_array($this->proxy) && 'ntlm' === $this->proxy['authtype'])) {
2368
            return 'curl';
2369
        }
2370
        if (('http' === $this->scheme || 'ssl' === $this->scheme) && 'ntlm' !== $this->authtype && (!is_array($this->proxy) || 'ntlm' !== $this->proxy['authtype'])) {
2371
            return 'socket';
2372
        }
2373
2374
        return 'unknown';
2375
    }
2376
2377
    /**
2378
     * Establish an HTTP connection
2379
     *
2380
     * @param int $connection_timeout set connection timeout in seconds
2381
     * @param int $response_timeout   set response timeout in seconds
2382
     * @return bool true if connected, false if not
2383
     */
2384
    private function connect($connection_timeout = 0, $response_timeout = 30)
2385
    {
2386
        // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2387
        // "regular" socket.
2388
        // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2389
        //       loaded), and until PHP5 stream_get_wrappers is not available.
2390
        //	  	if ($this->scheme == 'https') {
2391
        //          if (version_compare(PHP_VERSION, '4.3.0') >= 0) {
2392
        //		  		if (extension_loaded('openssl')) {
2393
        //		  			$this->scheme = 'ssl';
2394
        //		  			$this->debug('Using SSL over OpenSSL');
2395
        //		  		}
2396
        //		  	}
2397
        //		}
2398
        $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2399
        if ('socket' === $this->io_method()) {
2400
            if (!is_array($this->proxy)) {
2401
                $host = $this->host;
2402
                $port = $this->port;
0 ignored issues
show
Unused Code introduced by
The assignment to $port is dead and can be removed.
Loading history...
2403
            } else {
2404
                $host = $this->proxy['host'];
2405
                $port = $this->proxy['port'];
2406
            }
2407
2408
            // use persistent connection
2409
            if ($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) {
2410
                if (!feof($this->fp)) {
2411
                    $this->debug('Re-use persistent connection');
2412
2413
                    return true;
2414
                }
2415
                fclose($this->fp);
2416
                $this->debug('Closed persistent connection at EOF');
2417
            }
2418
2419
            // munge host if using OpenSSL
2420
            if ('ssl' === $this->scheme) {
2421
                $host = 'ssl://' . $host;
2422
            }
2423
            $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2424
            // open socket
2425
            if ($connection_timeout > 0) {
2426
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str, $connection_timeout);
0 ignored issues
show
Bug Best Practice introduced by
The property fp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug Best Practice introduced by
The property errno does not exist on Soap_transport_http. Did you maybe forget to declare it?
Loading history...
2427
            } else {
2428
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str);
2429
            }
2430
2431
            // test pointer
2432
            if (!$this->fp) {
2433
                $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2434
                if ($this->errno) {
2435
                    $msg .= ', Error (' . $this->errno . '): ' . $this->error_str;
2436
                } else {
2437
                    $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2438
                }
2439
                $this->debug($msg);
2440
                $this->setError($msg);
2441
2442
                return false;
2443
            }
2444
2445
            // set response timeout
2446
            $this->debug('set response timeout to ' . $response_timeout);
2447
            stream_set_timeout($this->fp, $response_timeout);
2448
            $this->debug('socket connected');
2449
2450
            return true;
2451
        }
2452
        if ('curl' === $this->io_method()) {
2453
            if (!extension_loaded('curl')) {
2454
                //			$this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2455
                $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.');
2456
2457
                return false;
2458
            }
2459
            // Avoid warnings when PHP does not have these options
2460
            $CURLOPT_CONNECTIONTIMEOUT = 78;
2461
            if (defined('CURLOPT_CONNECTIONTIMEOUT')) {
2462
                $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
0 ignored issues
show
Bug introduced by
The constant CURLOPT_CONNECTIONTIMEOUT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2463
            }
2464
            $CURLOPT_HTTPAUTH = 107;
2465
            if (defined('CURLOPT_HTTPAUTH')) {
2466
                $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2467
            }
2468
            $CURLOPT_PROXYAUTH = 111;
2469
            if (defined('CURLOPT_PROXYAUTH')) {
2470
                $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2471
            }
2472
            $CURLAUTH_BASIC = 1;
2473
            if (defined('CURLAUTH_BASIC')) {
2474
                $CURLAUTH_BASIC = CURLAUTH_BASIC;
2475
            }
2476
            $CURLAUTH_DIGEST = 2;
2477
            if (defined('CURLAUTH_DIGEST')) {
2478
                $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2479
            }
2480
            $CURLAUTH_NTLM = 8;
2481
            if (defined('CURLAUTH_NTLM')) {
2482
                $CURLAUTH_NTLM = CURLAUTH_NTLM;
2483
            }
2484
2485
            $this->debug('connect using cURL');
2486
            // init CURL
2487
            $this->ch = curl_init();
2488
            // set url
2489
            $hostURL = (0 !== $this->port) ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2490
            // add path
2491
            $hostURL .= $this->path;
2492
            $this->setCurlOption(CURLOPT_URL, $hostURL);
2493
            // follow location headers (re-directs)
2494
            $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2495
            // ask for headers in the response output
2496
            $this->setCurlOption(CURLOPT_HEADER, 1);
2497
            // ask for the response output as the return value
2498
            $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2499
            // encode
2500
            // We manage this ourselves through headers and encoding
2501
            //		if(function_exists('gzuncompress')){
2502
            //			$this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2503
            //		}
2504
            // persistent connection
2505
            if ($this->persistentConnection) {
2506
                // I believe the following comment is now bogus, having applied to
2507
                // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2508
                // The way we send data, we cannot use persistent connections, since
2509
                // there will be some "junk" at the end of our request.
2510
                //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2511
                $this->persistentConnection = false;
2512
                $this->setHeader('Connection', 'close');
2513
            }
2514
            // set timeouts
2515
            if (0 !== $connection_timeout) {
2516
                $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2517
            }
2518
            if (0 !== $response_timeout) {
2519
                $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2520
            }
2521
2522
            if ('https' === $this->scheme) {
2523
                $this->debug('set cURL SSL verify options');
2524
                // recent versions of cURL turn on peer/host checking by default,
2525
                // while PHP binaries are not compiled with a default location for the
2526
                // CA cert bundle, so disable peer/host checking.
2527
                //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2528
                $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2529
                $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2530
                // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2531
                if ('certificate' === $this->authtype) {
2532
                    $this->debug('set cURL certificate options');
2533
                    if (isset($this->certRequest['cainfofile'])) {
2534
                        $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2535
                    }
2536
                    if (isset($this->certRequest['verifypeer'])) {
2537
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2538
                    } else {
2539
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2540
                    }
2541
                    if (isset($this->certRequest['verifyhost'])) {
2542
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2543
                    } else {
2544
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2545
                    }
2546
                    if (isset($this->certRequest['sslcertfile'])) {
2547
                        $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2548
                    }
2549
                    if (isset($this->certRequest['sslkeyfile'])) {
2550
                        $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2551
                    }
2552
                    if (isset($this->certRequest['passphrase'])) {
2553
                        $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2554
                    }
2555
                    if (isset($this->certRequest['certpassword'])) {
2556
                        $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2557
                    }
2558
                }
2559
            }
2560
            if ($this->authtype && ('certificate' !== $this->authtype)) {
2561
                if ($this->username) {
2562
                    $this->debug('set cURL username/password');
2563
                    $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2564
                }
2565
                if ('basic' === $this->authtype) {
2566
                    $this->debug('set cURL for Basic authentication');
2567
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2568
                }
2569
                if ('digest' === $this->authtype) {
2570
                    $this->debug('set cURL for digest authentication');
2571
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2572
                }
2573
                if ('ntlm' === $this->authtype) {
2574
                    $this->debug('set cURL for NTLM authentication');
2575
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2576
                }
2577
            }
2578
            if (is_array($this->proxy)) {
2579
                $this->debug('set cURL proxy options');
2580
                if ('' !== $this->proxy['port']) {
2581
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'] . ':' . $this->proxy['port']);
2582
                } else {
2583
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2584
                }
2585
                if ($this->proxy['username'] || $this->proxy['password']) {
2586
                    $this->debug('set cURL proxy authentication options');
2587
                    $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'] . ':' . $this->proxy['password']);
2588
                    if ('basic' === $this->proxy['authtype']) {
2589
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2590
                    }
2591
                    if ('ntlm' === $this->proxy['authtype']) {
2592
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2593
                    }
2594
                }
2595
            }
2596
            $this->debug('cURL connection set up');
2597
2598
            return true;
2599
        }
2600
            $this->setError('Unknown scheme ' . $this->scheme);
2601
            $this->debug('Unknown scheme ' . $this->scheme);
2602
2603
            return false;
2604
        }
2605
2606
    /**
2607
     * Sends the SOAP request and gets the SOAP response via HTTP[S]
2608
     *
2609
     * @param    string $data             message data
2610
     * @param int       $timeout          set connection timeout in seconds
2611
     * @param int       $response_timeout set response timeout in seconds
2612
     * @param    array  $cookies          cookies to send
2613
     * @return    string|bool data
2614
     */
2615
    public function send($data, $timeout = 0, $response_timeout = 30, $cookies = null)
2616
    {
2617
        $this->debug('entered send() with data of length: ' . mb_strlen($data));
2618
        $this->tryagain = true;
2619
        $tries          = 0;
2620
        $respdata = '';
2621
        while ($this->tryagain) {
2622
            $this->tryagain = false;
2623
            if (++$tries < 2) {
2624
                // make connnection
2625
                if (!$this->connect($timeout, $response_timeout)) {
2626
                    return false;
2627
                }
2628
2629
                // send request
2630
                if (!$this->sendRequest($data, $cookies)) {
2631
                    return false;
2632
                }
2633
2634
                // get response
2635
                $respdata = $this->getResponse();
2636
            } else {
2637
                $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2638
            }
2639
        }
2640
        $this->debug('end of send()');
2641
2642
        return $respdata;
2643
    }
2644
2645
    /**
2646
     * Sends the SOAP request and gets the SOAP response via HTTPS using CURL
2647
     *
2648
     * @param    string $data             message data
2649
     * @param int       $timeout          set connection timeout in seconds
2650
     * @param int       $response_timeout set response timeout in seconds
2651
     * @param    array  $cookies          cookies to send
2652
     * @return    string|bool data
2653
     * @deprecated
2654
     */
2655
    public function sendHTTPS($data, $timeout = 0, $response_timeout = 30, $cookies = [])
2656
    {
2657
        return $this->send($data, $timeout, $response_timeout, $cookies);
2658
    }
2659
2660
    /**
2661
     * If authenticating, set user credentials here
2662
     *
2663
     * @param    string $username
2664
     * @param    string $password
2665
     * @param    string $authtype      (basic|digest|certificate|ntlm)
2666
     * @param    array  $digestRequest (keys must be nonce, nc, realm, qop)
2667
     * @param    array  $certRequest   (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2668
     */
2669
    public function setCredentials($username, $password, $authtype = 'basic', $digestRequest = [], $certRequest = [])
2670
    {
2671
        $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2672
        $this->appendDebug($this->varDump($digestRequest));
2673
        $this->debug('certRequest=');
2674
        $this->appendDebug($this->varDump($certRequest));
2675
        // cf. RFC 2617
2676
        if ('basic' === $authtype) {
2677
            $this->setHeader('Authorization', 'Basic ' . base64_encode(str_replace(':', '', $username) . ':' . $password));
2678
        } elseif ('digest' === $authtype) {
2679
            if (isset($digestRequest['nonce'])) {
2680
                $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2681
                // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2682
2683
                // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2684
                $A1 = $username . ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2685
2686
                // H(A1) = MD5(A1)
2687
                $HA1 = md5($A1);
2688
                // A2 = Method ":" digest-uri-value
2689
                $A2 = $this->request_method . ':' . $this->digest_uri;
0 ignored issues
show
Bug introduced by
Are you sure $this->digest_uri of type mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2689
                $A2 = $this->request_method . ':' . /** @scrutinizer ignore-type */ $this->digest_uri;
Loading history...
2690
                // H(A2)
2691
                $HA2 = md5($A2);
2692
                // KD(secret, data) = H(concat(secret, ":", data))
2693
                // if qop == auth:
2694
                // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2695
                //                              ":" nc-value
2696
                //                              ":" unq(cnonce-value)
2697
                //                              ":" unq(qop-value)
2698
                //                              ":" H(A2)
2699
                //                            ) <">
2700
                // if qop is missing,
2701
                // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2702
2703
                $unhashedDigest = '';
2704
                $nonce          = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2705
                $cnonce         = $nonce;
2706
                if ('' !== $digestRequest['qop']) {
2707
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf('%08d', $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2708
                } else {
2709
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2710
                }
2711
2712
                $hashedDigest = md5($unhashedDigest);
2713
                $opaque       = '';
2714
                if (isset($digestRequest['opaque'])) {
2715
                    $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2716
                }
2717
2718
                $this->setHeader('Authorization', 'Digest username="'
2719
                                                  . $username
2720
                                                  . '", realm="'
2721
                                                  . $digestRequest['realm']
2722
                                                  . '", nonce="'
2723
                                                  . $nonce
2724
                                                  . '", uri="'
2725
                                                  . $this->digest_uri
2726
                                                  . $opaque
2727
                                                  . '", cnonce="'
2728
                                                  . $cnonce
2729
                                                  . '", nc='
2730
                                                  . sprintf('%08x', $digestRequest['nc'])
2731
                                                  . ', qop="'
2732
                                                  . $digestRequest['qop']
2733
                                                  . '", response="'
2734
                                                  . $hashedDigest
2735
                                                  . '"');
2736
            }
2737
        } elseif ('certificate' === $authtype) {
2738
            $this->certRequest = $certRequest;
2739
            $this->debug('Authorization header not set for certificate');
2740
        } elseif ('ntlm' === $authtype) {
2741
            // do nothing
2742
            $this->debug('Authorization header not set for ntlm');
2743
        }
2744
        $this->username      = $username;
2745
        $this->password      = $password;
2746
        $this->authtype      = $authtype;
2747
        $this->digestRequest = $digestRequest;
2748
    }
2749
2750
    /**
2751
     * Set the soapaction value
2752
     *
2753
     * @param    string $soapaction
2754
     */
2755
    public function setSOAPAction($soapaction)
2756
    {
2757
        $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2758
    }
2759
2760
    /**
2761
     * Use http encoding
2762
     *
2763
     * @param    string $enc encoding style. supported values: gzip, deflate, or both
2764
     */
2765
    public function setEncoding($enc = 'gzip, deflate')
2766
    {
2767
        if (function_exists('gzdeflate')) {
2768
            $this->protocol_version = '1.1';
2769
            $this->setHeader('Accept-Encoding', $enc);
2770
            if (!isset($this->outgoing_headers['Connection'])) {
2771
                $this->setHeader('Connection', 'close');
2772
                $this->persistentConnection = false;
2773
            }
2774
            // deprecated as of PHP 5.3.0
2775
            //set_magic_quotes_runtime(0);
2776
            $this->encoding = $enc;
2777
        }
2778
    }
2779
2780
    /**
2781
     * Set proxy info here
2782
     *
2783
     * @param    bool|string $proxyhost     use an empty string to remove proxy
2784
     * @param    bool|string $proxyport
2785
     * @param    bool|string $proxyusername
2786
     * @param    bool|string $proxypassword
2787
     * @param    bool|string $proxyauthtype (basic|ntlm)
2788
     */
2789
    public function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic')
2790
    {
2791
        if ($proxyhost) {
2792
            $this->proxy = [
2793
                'host'     => $proxyhost,
2794
                'port'     => $proxyport,
2795
                'username' => $proxyusername,
2796
                'password' => $proxypassword,
2797
                'authtype' => $proxyauthtype,
2798
            ];
2799
            if ('' !== $proxyusername && '' !== $proxypassword && $proxyauthtype = 'basic') {
0 ignored issues
show
Unused Code introduced by
The assignment to $proxyauthtype is dead and can be removed.
Loading history...
2800
                $this->setHeader('Proxy-Authorization', ' Basic ' . base64_encode($proxyusername . ':' . $proxypassword));
2801
            }
2802
        } else {
2803
            $this->debug('remove proxy');
2804
            $proxy = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $proxy is dead and can be removed.
Loading history...
2805
            $this->unsetHeader('Proxy-Authorization');
2806
        }
2807
    }
2808
2809
    /**
2810
     * Test if the given string starts with a header that is to be skipped.
2811
     * Skippable headers result from chunked transfer and proxy requests.
2812
     *
2813
     * @param    string $data The string to check.
2814
     * @returns    boolean    Whether a skippable header was found.
2815
     * @return bool
2816
     */
2817
    private function isSkippableCurlHeader(&$data)
2818
    {
2819
        $skipHeaders = [
2820
            'HTTP/1.1 100',
2821
            'HTTP/1.0 301',
2822
            'HTTP/1.1 301',
2823
            'HTTP/1.0 302',
2824
            'HTTP/1.1 302',
2825
            'HTTP/1.0 401',
2826
            'HTTP/1.1 401',
2827
            'HTTP/1.0 200 Connection established',
2828
        ];
2829
        foreach ($skipHeaders as $hd) {
2830
            $prefix = mb_substr($data, 0, mb_strlen($hd));
2831
            if ($prefix === $hd) {
2832
                return true;
2833
            }
2834
        }
2835
2836
        return false;
2837
    }
2838
2839
    /**
2840
     * Decode a string that is encoded w/ "chunked' transfer encoding
2841
     * as defined in RFC2068 19.4.6
2842
     *
2843
     * @param    string $buffer
2844
     * @param    string $lb
2845
     * @returns    string
2846
     * @deprecated
2847
     * @return string
2848
     */
2849
    public function decodeChunked($buffer, $lb)
2850
    {
2851
        // length := 0
2852
        $length = 0;
2853
        $new    = '';
2854
        // read chunk-size, chunk-extension (if any) and CRLF
2855
        // get the position of the linebreak
2856
        $chunkend = mb_strpos($buffer, $lb);
2857
        if (false === $chunkend) {
2858
            $this->debug('no linebreak found in decodeChunked');
2859
2860
            return $new;
2861
        }
2862
        $temp       = mb_substr($buffer, 0, $chunkend);
2863
        $chunk_size = hexdec(trim($temp));
2864
        $chunkstart = $chunkend + mb_strlen($lb);
2865
        // while (chunk-size > 0) {
2866
        while ($chunk_size > 0) {
2867
            $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2868
            $chunkend = mb_strpos($buffer, $lb, $chunkstart + $chunk_size);
0 ignored issues
show
Bug introduced by
$chunkstart + $chunk_size of type double is incompatible with the type integer expected by parameter $offset of mb_strpos(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2868
            $chunkend = mb_strpos($buffer, $lb, /** @scrutinizer ignore-type */ $chunkstart + $chunk_size);
Loading history...
2869
            // Just in case we got a broken connection
2870
            if (false === $chunkend) {
2871
                $chunk = mb_substr($buffer, $chunkstart);
2872
                // append chunk-data to entity-body
2873
                $new    .= $chunk;
2874
                $length += mb_strlen($chunk);
2875
                break;
2876
            }
2877
2878
            // read chunk-data and CRLF
2879
            $chunk = mb_substr($buffer, $chunkstart, $chunkend - $chunkstart);
2880
            // append chunk-data to entity-body
2881
            $new .= $chunk;
2882
            // length := length + chunk-size
2883
            $length += mb_strlen($chunk);
2884
            // read chunk-size and CRLF
2885
            $chunkstart = $chunkend + mb_strlen($lb);
2886
            $chunkend   = mb_strpos($buffer, $lb, $chunkstart) + mb_strlen($lb);
2887
            if (false === $chunkend) {
2888
                break; //Just in case we got a broken connection
2889
            }
2890
            $temp       = mb_substr($buffer, $chunkstart, $chunkend - $chunkstart);
2891
            $chunk_size = hexdec(trim($temp));
2892
            $chunkstart = $chunkend;
2893
        }
2894
2895
        return $new;
2896
    }
2897
2898
    /**
2899
     * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2900
     *
2901
     * @param    string $data       HTTP body
2902
     * @param    string $cookie_str data for HTTP Cookie header
2903
     */
2904
    private function buildPayload($data, $cookie_str = '')
2905
    {
2906
        // Note: for cURL connections, $this->outgoing_payload is ignored,
2907
        // as is the Content-Length header, but these are still created as
2908
        // debugging guides.
2909
2910
        // add content-length header
2911
        if ('GET' !== $this->request_method) {
2912
            $this->setHeader('Content-Length', mb_strlen($data));
2913
        }
2914
2915
        // start building outgoing payload:
2916
        if ($this->proxy) {
2917
            $uri = $this->url;
2918
        } else {
2919
            $uri = $this->uri;
2920
        }
2921
        $req = "$this->request_method $uri HTTP/$this->protocol_version";
2922
        $this->debug("HTTP request: $req");
2923
        $this->outgoing_payload = "$req\r\n";
2924
        // loop thru headers, serializing
2925
        foreach ($this->outgoing_headers as $k => $v) {
2926
            $hdr = $k . ': ' . $v;
2927
            $this->debug("HTTP header: $hdr");
2928
            $this->outgoing_payload .= "$hdr\r\n";
2929
        }
2930
2931
        // add any cookies
2932
        if ('' !== $cookie_str) {
2933
            $hdr = 'Cookie: ' . $cookie_str;
2934
            $this->debug("HTTP header: $hdr");
2935
            $this->outgoing_payload .= "$hdr\r\n";
2936
        }
2937
2938
        // header/body separator
2939
        $this->outgoing_payload .= "\r\n";
2940
        // add data
2941
        $this->outgoing_payload .= $data;
2942
    }
2943
2944
    /**
2945
     * Sends the SOAP request via HTTP[S]
2946
     *
2947
     * @param    string $data    message data
2948
     * @param    array  $cookies cookies to send
2949
     * @return bool    true if OK, false if problem
2950
     */
2951
    private function sendRequest($data, $cookies = null)
2952
    {
2953
        // build cookie string
2954
        $cookie_str = $this->getCookiesForRequest($cookies, ('ssl' === $this->scheme) || ('https' === $this->scheme));
2955
        // build payload
2956
        $this->buildPayload($data, $cookie_str);
2957
        if ('socket' === $this->io_method()) {
2958
            // send payload
2959
            if (!fwrite($this->fp, $this->outgoing_payload, mb_strlen($this->outgoing_payload))) {
2960
                $this->setError('couldn\'t write message data to socket');
2961
                $this->debug('couldn\'t write message data to socket');
2962
2963
                return false;
2964
            }
2965
            $this->debug('wrote data to socket, length = ' . mb_strlen($this->outgoing_payload));
2966
2967
            return true;
2968
        }
2969
2970
        if ('curl' === $this->io_method()) {
2971
            // set payload
2972
            // cURL does say this should only be the verb, and in fact it
2973
            // turns out that the URI and HTTP version are appended to this, which
2974
            // some servers refuse to work with (so we no longer use this method!)
2975
            //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2976
            $curl_headers = [];
2977
            foreach ($this->outgoing_headers as $k => $v) {
2978
                if ('Connection' === $k || 'Content-Length' === $k || 'Host' === $k || 'Authorization' === $k || 'Proxy-Authorization' === $k) {
2979
                    $this->debug("Skip cURL header $k: $v");
2980
                } else {
2981
                    $curl_headers[] = "$k: $v";
2982
                }
2983
            }
2984
            if ('' !== $cookie_str) {
2985
                $curl_headers[] = 'Cookie: ' . $cookie_str;
2986
            }
2987
            $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2988
            $this->debug('set cURL HTTP headers');
2989
            if ('POST' === $this->request_method) {
2990
                $this->setCurlOption(CURLOPT_POST, 1);
2991
                $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2992
                $this->debug('set cURL POST data');
2993
            }
2994
2995
            // insert custom user-set cURL options
2996
            foreach ($this->ch_options as $key => $val) {
2997
                $this->setCurlOption($key, $val);
2998
            }
2999
3000
            $this->debug('set cURL payload');
3001
3002
            return true;
3003
        }
3004
    }
3005
3006
    /**
3007
     * Gets the SOAP response via HTTP[S]
3008
     *
3009
     * @return    string|bool the response (also sets member variables like incoming_payload)
3010
     */
3011
    private function getResponse()
3012
    {
3013
        $header_array = [];
3014
        $this->incoming_payload = '';
3015
        if ('socket' === $this->io_method()) {
3016
            // loop until headers have been retrieved
3017
            $data = '';
3018
            while (!isset($lb)) {
3019
                // We might EOF during header read.
3020
                if (feof($this->fp)) {
3021
                    $this->incoming_payload = $data;
3022
                    $this->debug('found no headers before EOF after length ' . mb_strlen($data));
3023
                    $this->debug("received before EOF:\n" . $data);
3024
                    $this->setError('server failed to send headers');
3025
3026
                    return false;
3027
                }
3028
3029
                $tmp    = fgets($this->fp, 256);
3030
                $tmplen = mb_strlen($tmp);
3031
                $this->debug("read line of $tmplen bytes: " . trim($tmp));
3032
                if (0 === $tmplen) {
3033
                    $this->incoming_payload = $data;
3034
                    $this->debug('socket read of headers timed out after length ' . mb_strlen($data));
3035
                    $this->debug('read before timeout: ' . $data);
3036
                    $this->setError('socket read of headers timed out');
3037
3038
                    return false;
3039
                }
3040
3041
                $data .= $tmp;
3042
                $pos  = mb_strpos($data, "\r\n\r\n");
3043
                if ($pos > 1) {
3044
                    $lb = "\r\n";
3045
                } else {
3046
                    $pos = mb_strpos($data, "\n\n");
3047
                    if ($pos > 1) {
3048
                        $lb = "\n";
3049
                    }
3050
                }
3051
                // remove 100 headers
3052
                if (isset($lb) && preg_match('/^HTTP\/1.1 100/', $data)) {
3053
                    unset($lb);
3054
                    $data = '';
3055
                }
3056
            }
3057
            // store header data
3058
            $this->incoming_payload .= $data;
3059
            $this->debug('found end of headers after length ' . mb_strlen($data));
3060
            // process headers
3061
            $header_data            = trim(mb_substr($data, 0, $pos));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pos does not seem to be defined for all execution paths leading up to this point.
Loading history...
3062
            $header_array           = explode($lb, $header_data);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $lb does not seem to be defined for all execution paths leading up to this point.
Loading history...
3063
            $this->incoming_headers = [];
3064
            $this->incoming_cookies = [];
3065
            foreach ($header_array as $header_line) {
3066
                $arr = explode(':', $header_line, 2);
3067
                if (is_array($arr) && count($arr) > 1) {
3068
                    $header_name                          = mb_strtolower(trim($arr[0]));
3069
                    $this->incoming_headers[$header_name] = trim($arr[1]);
3070
                    if ('set-cookie' === $header_name) {
3071
                        // TODO: allow multiple cookies from parseCookie
3072
                        $cookie = $this->parseCookie(trim($arr[1]));
3073
                        if ($cookie) {
3074
                            $this->incoming_cookies[] = $cookie;
3075
                            $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3076
                        } else {
3077
                            $this->debug('did not find cookie in ' . trim($arr[1]));
3078
                        }
3079
                    }
3080
                } elseif (isset($header_name)) {
3081
                    // append continuation line to previous header
3082
                    $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3083
                }
3084
            }
3085
3086
            // loop until msg has been received
3087
            if (isset($this->incoming_headers['transfer-encoding']) && 'chunked' === mb_strtolower($this->incoming_headers['transfer-encoding'])) {
3088
                $content_length = 2147483647;    // ignore any content-length header
3089
                $chunked        = true;
3090
                $this->debug('want to read chunked content');
3091
            } elseif (isset($this->incoming_headers['content-length'])) {
3092
                $content_length = $this->incoming_headers['content-length'];
3093
                $chunked        = false;
3094
                $this->debug("want to read content of length $content_length");
3095
            } else {
3096
                $content_length = 2147483647;
3097
                $chunked        = false;
3098
                $this->debug('want to read content to EOF');
3099
            }
3100
            $data = '';
3101
            do {
3102
                if ($chunked) {
3103
                    $tmp    = fgets($this->fp, 256);
3104
                    $tmplen = mb_strlen($tmp);
3105
                    $this->debug("read chunk line of $tmplen bytes");
3106
                    if (0 === $tmplen) {
3107
                        $this->incoming_payload = $data;
3108
                        $this->debug('socket read of chunk length timed out after length ' . mb_strlen($data));
3109
                        $this->debug("read before timeout:\n" . $data);
3110
                        $this->setError('socket read of chunk length timed out');
3111
3112
                        return false;
3113
                    }
3114
                    $content_length = hexdec(trim($tmp));
3115
                    $this->debug("chunk length $content_length");
3116
                }
3117
                $strlen = 0;
3118
                while (($strlen < $content_length) && (!feof($this->fp))) {
3119
                    $readlen = min(8192, $content_length - $strlen);
3120
                    $tmp     = fread($this->fp, $readlen);
3121
                    $tmplen  = mb_strlen($tmp);
3122
                    $this->debug("read buffer of $tmplen bytes");
3123
                    if ((0 === $tmplen) && (!feof($this->fp))) {
3124
                        $this->incoming_payload = $data;
3125
                        $this->debug('socket read of body timed out after length ' . mb_strlen($data));
3126
                        $this->debug("read before timeout:\n" . $data);
3127
                        $this->setError('socket read of body timed out');
3128
3129
                        return false;
3130
                    }
3131
                    $strlen += $tmplen;
3132
                    $data   .= $tmp;
3133
                }
3134
                if ($chunked && ($content_length > 0)) {
3135
                    $tmp    = fgets($this->fp, 256);
3136
                    $tmplen = mb_strlen($tmp);
3137
                    $this->debug("read chunk terminator of $tmplen bytes");
3138
                    if (0 === $tmplen) {
3139
                        $this->incoming_payload = $data;
3140
                        $this->debug('socket read of chunk terminator timed out after length ' . mb_strlen($data));
3141
                        $this->debug("read before timeout:\n" . $data);
3142
                        $this->setError('socket read of chunk terminator timed out');
3143
3144
                        return false;
3145
                    }
3146
                }
3147
            } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3148
            if (feof($this->fp)) {
3149
                $this->debug('read to EOF');
3150
            }
3151
            $this->debug('read body of length ' . mb_strlen($data));
3152
            $this->incoming_payload .= $data;
3153
            $this->debug('received a total of ' . mb_strlen($this->incoming_payload) . ' bytes of data from server');
3154
            // close filepointer
3155
            if ((isset($this->incoming_headers['connection']) && 'close' === mb_strtolower($this->incoming_headers['connection'])) || (!$this->persistentConnection) || feof($this->fp)) {
3156
                fclose($this->fp);
3157
                $this->fp = false;
0 ignored issues
show
Bug Best Practice introduced by
The property fp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
3158
                $this->debug('closed socket');
3159
            }
3160
3161
            // connection was closed unexpectedly
3162
            if ('' === $this->incoming_payload) {
3163
                $this->setError('no response from server');
3164
3165
                return false;
3166
            }
3167
3168
            // decode transfer-encoding
3169
            //		if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3170
            //			if(!$data = $this->decodeChunked($data, $lb)){
3171
            //				$this->setError('Decoding of chunked data failed');
3172
            //				return false;
3173
            //			}
3174
            //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3175
            // set decoded payload
3176
            //			$this->incoming_payload = $header_data.$lb.$lb.$data;
3177
            //		}
3178
        } elseif ('curl' === $this->io_method()) {
3179
            // send and receive
3180
            $this->debug('send and receive with cURL');
3181
            $this->incoming_payload = curl_exec($this->ch);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type resource expected by parameter $ch of curl_exec(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3181
            $this->incoming_payload = curl_exec(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3182
            $data                   = $this->incoming_payload;
3183
            $cErr                   = curl_error($this->ch);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type resource expected by parameter $ch of curl_error(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3183
            $cErr                   = curl_error(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3184
            if ('' !== $cErr) {
3185
                $err = 'cURL ERROR: ' . curl_errno($this->ch) . ': ' . $cErr . '<br>';
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type resource expected by parameter $ch of curl_errno(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3185
                $err = 'cURL ERROR: ' . curl_errno(/** @scrutinizer ignore-type */ $this->ch) . ': ' . $cErr . '<br>';
Loading history...
3186
                // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3187
                foreach (curl_getinfo($this->ch) as $k => $v) {
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type resource expected by parameter $ch of curl_getinfo(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3187
                foreach (curl_getinfo(/** @scrutinizer ignore-type */ $this->ch) as $k => $v) {
Loading history...
3188
                    $err .= "$k: $v<br>";
3189
                }
3190
                $this->debug($err);
3191
                $this->setError($err);
3192
                curl_close($this->ch);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type resource expected by parameter $ch of curl_close(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3192
                curl_close(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3193
3194
                return false;
3195
            }
3196
3197
            //echo '<pre>';
3198
            //var_dump(curl_getinfo($this->ch));
3199
            //echo '</pre>';
3200
            // close curl
3201
            $this->debug('No cURL error, closing cURL');
3202
            curl_close($this->ch);
3203
            // try removing skippable headers
3204
            $savedata = $data;
3205
            while ($this->isSkippableCurlHeader($data)) {
3206
                $this->debug('Found HTTP header to skip');
3207
                if (false !== ($pos = mb_strpos($data, "\r\n\r\n"))) {
3208
                    $data = ltrim(mb_substr($data, $pos));
3209
                } elseif (false !== ($pos = mb_strpos($data, "\n\n"))) {
3210
                    $data = ltrim(mb_substr($data, $pos));
3211
                }
3212
            }
3213
3214
            if ('' === $data) {
3215
                // have nothing left; just remove 100 header(s)
3216
                $data = $savedata;
3217
                while (preg_match('/^HTTP\/1.1 100/', $data)) {
3218
                    if (false !== ($pos = mb_strpos($data, "\r\n\r\n"))) {
3219
                        $data = ltrim(mb_substr($data, $pos));
3220
                    } elseif (false !== ($pos = mb_strpos($data, "\n\n"))) {
3221
                        $data = ltrim(mb_substr($data, $pos));
3222
                    }
3223
                }
3224
            }
3225
3226
            // separate content from HTTP headers
3227
            if (false !== ($pos = mb_strpos($data, "\r\n\r\n"))) {
3228
                $lb = "\r\n";
3229
            } elseif (false !== ($pos = mb_strpos($data, "\n\n"))) {
3230
                $lb = "\n";
3231
            } else {
3232
                $this->debug('no proper separation of headers and document');
3233
                $this->setError('no proper separation of headers and document');
3234
3235
                return false;
3236
            }
3237
            $header_data  = trim(mb_substr($data, 0, $pos));
3238
            $header_array = explode($lb, $header_data);
3239
            $data         = ltrim(mb_substr($data, $pos));
3240
            $this->debug('found proper separation of headers and document');
3241
            $this->debug('cleaned data, stringlen: ' . mb_strlen($data));
3242
            // clean headers
3243
            foreach ($header_array as $header_line) {
3244
                $arr = explode(':', $header_line, 2);
3245
                if (is_array($arr) && count($arr) > 1) {
3246
                    $header_name                          = mb_strtolower(trim($arr[0]));
3247
                    $this->incoming_headers[$header_name] = trim($arr[1]);
3248
                    if ('set-cookie' === $header_name) {
3249
                        // TODO: allow multiple cookies from parseCookie
3250
                        $cookie = $this->parseCookie(trim($arr[1]));
3251
                        if ($cookie) {
3252
                            $this->incoming_cookies[] = $cookie;
3253
                            $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3254
                        } else {
3255
                            $this->debug('did not find cookie in ' . trim($arr[1]));
3256
                        }
3257
                    }
3258
                } elseif (isset($header_name)) {
3259
                    // append continuation line to previous header
3260
                    $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3261
                }
3262
            }
3263
        }
3264
3265
        $this->response_status_line = $header_array[0];
3266
        $arr                        = explode(' ', $this->response_status_line, 3);
3267
        $http_version               = $arr[0];
0 ignored issues
show
Unused Code introduced by
The assignment to $http_version is dead and can be removed.
Loading history...
3268
        $http_status                = (int)$arr[1];
3269
        $http_reason                = count($arr) > 2 ? $arr[2] : '';
3270
        // see if we need to resend the request with http digest authentication
3271
        if (isset($this->incoming_headers['location']) && (301 === $http_status || 302 === $http_status)) {
3272
            $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3273
            $this->setURL($this->incoming_headers['location']);
3274
            $this->tryagain = true;
3275
3276
            return false;
3277
        }
3278
3279
        // see if we need to resend the request with http digest authentication
3280
        if (isset($this->incoming_headers['www-authenticate']) && 401 === $http_status) {
3281
            $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3282
            if (false !== mb_strpos($this->incoming_headers['www-authenticate'], 'Digest ')) {
3283
                $this->debug('Server wants digest authentication');
3284
                // remove "Digest " from our elements
3285
                $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3286
                // parse elements into array
3287
                $digestElements = explode(',', $digestString);
3288
                foreach ($digestElements as $val) {
3289
                    $tempElement                    = explode('=', trim($val), 2);
3290
                    $digestRequest[$tempElement[0]] = str_replace('"', '', $tempElement[1]);
3291
                }
3292
3293
                // should have (at least) qop, realm, nonce
3294
                if (isset($digestRequest['nonce'])) {
3295
                    $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $digestRequest seems to be defined by a foreach iteration on line 3288. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
3296
                    $this->tryagain = true;
3297
3298
                    return false;
3299
                }
3300
            }
3301
            $this->debug('HTTP authentication failed');
3302
            $this->setError('HTTP authentication failed');
3303
3304
            return false;
3305
        }
3306
3307
        if (($http_status >= 300 && $http_status <= 307) || ($http_status >= 400 && $http_status <= 417) || ($http_status >= 501 && $http_status <= 505)) {
3308
            $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3309
3310
            return false;
3311
        }
3312
3313
        // decode content-encoding
3314
        if (isset($this->incoming_headers['content-encoding']) && '' !== $this->incoming_headers['content-encoding']) {
3315
            if ('deflate' === mb_strtolower($this->incoming_headers['content-encoding']) || 'gzip' === mb_strtolower($this->incoming_headers['content-encoding'])) {
3316
                // if decoding works, use it. else assume data wasn't gzencoded
3317
                if (function_exists('gzinflate')) {
3318
                    //$timer->setMarker('starting decoding of gzip/deflated content');
3319
                    // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3320
                    // this means there are no Zlib headers, although there should be
3321
                    $this->debug('The gzinflate function exists');
3322
                    $datalen = mb_strlen($data);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.
Loading history...
3323
                    if ('deflate' === $this->incoming_headers['content-encoding']) {
3324
                        if (false !== ($degzdata = @gzinflate($data))) {
3325
                            $data = $degzdata;
3326
                            $this->debug('The payload has been inflated to ' . mb_strlen($data) . ' bytes');
3327
                            if (mb_strlen($data) < $datalen) {
3328
                                // test for the case that the payload has been compressed twice
3329
                                $this->debug('The inflated payload is smaller than the gzipped one; try again');
3330
                                if (false !== ($degzdata = @gzinflate($data))) {
3331
                                    $data = $degzdata;
3332
                                    $this->debug('The payload has been inflated again to ' . mb_strlen($data) . ' bytes');
3333
                                }
3334
                            }
3335
                        } else {
3336
                            $this->debug('Error using gzinflate to inflate the payload');
3337
                            $this->setError('Error using gzinflate to inflate the payload');
3338
                        }
3339
                    } elseif ('gzip' === $this->incoming_headers['content-encoding']) {
3340
                        if (false !== ($degzdata = @gzinflate(mb_substr($data, 10)))) {
3341
                            // do our best
3342
                            $data = $degzdata;
3343
                            $this->debug('The payload has been un-gzipped to ' . mb_strlen($data) . ' bytes');
3344
                            if (mb_strlen($data) < $datalen) {
3345
                                // test for the case that the payload has been compressed twice
3346
                                $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3347
                                if (false !== ($degzdata = @gzinflate(mb_substr($data, 10)))) {
3348
                                    $data = $degzdata;
3349
                                    $this->debug('The payload has been un-gzipped again to ' . mb_strlen($data) . ' bytes');
3350
                                }
3351
                            }
3352
                        } else {
3353
                            $this->debug('Error using gzinflate to un-gzip the payload');
3354
                            $this->setError('Error using gzinflate to un-gzip the payload');
3355
                        }
3356
                    }
3357
                    //$timer->setMarker('finished decoding of gzip/deflated content');
3358
                    //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3359
                    // set decoded payload
3360
                    $this->incoming_payload = $header_data . $lb . $lb . $data;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $header_data does not seem to be defined for all execution paths leading up to this point.
Loading history...
3361
                } else {
3362
                    $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3363
                    $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3364
                }
3365
            } else {
3366
                $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3367
                $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3368
            }
3369
        } else {
3370
            $this->debug('No Content-Encoding header');
3371
        }
3372
3373
        if ('' === $data) {
3374
            $this->debug('no data after headers!');
3375
            $this->setError('no data present after HTTP headers');
3376
3377
            return false;
3378
        }
3379
3380
        return $data;
3381
    }
3382
3383
    /**
3384
     * Sets the content-type for the SOAP message to be sent
3385
     *
3386
     * @param    string $type    the content type, MIME style
3387
     * @param    mixed  $charset character set used for encoding (or false)
3388
     */
3389
    public function setContentType($type, $charset = false)
3390
    {
3391
        $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3392
    }
3393
3394
    /**
3395
     * Specifies that an HTTP persistent connection should be used
3396
     *
3397
     * @return bool whether the request was honored by this method.
3398
     */
3399
    public function usePersistentConnection()
3400
    {
3401
        if (isset($this->outgoing_headers['Accept-Encoding'])) {
3402
            return false;
3403
        }
3404
        $this->protocol_version     = '1.1';
3405
        $this->persistentConnection = true;
3406
        $this->setHeader('Connection', 'Keep-Alive');
3407
3408
        return true;
3409
    }
3410
3411
    /**
3412
     * Parse an incoming Cookie into it's parts
3413
     *
3414
     * @param    string $cookie_str content of cookie
3415
     * @return    bool|array with data of that cookie
3416
     */
3417
    /*
3418
     * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3419
     */
3420
    private function parseCookie($cookie_str)
3421
    {
3422
        $cookie_str   = str_replace('; ', ';', $cookie_str) . ';';
3423
        $data         = preg_split('/;/', $cookie_str);
3424
        $value_str    = $data[0];
3425
        $cookie_param = 'domain=';
3426
        $start        = mb_strpos($cookie_str, $cookie_param);
3427
        if ($start > 0) {
3428
            $domain = mb_substr($cookie_str, $start + mb_strlen($cookie_param));
3429
            $domain = mb_substr($domain, 0, mb_strpos($domain, ';'));
3430
        } else {
3431
            $domain = '';
3432
        }
3433
3434
        $cookie_param = 'expires=';
3435
        $start        = mb_strpos($cookie_str, $cookie_param);
3436
        if ($start > 0) {
3437
            $expires = mb_substr($cookie_str, $start + mb_strlen($cookie_param));
3438
            $expires = mb_substr($expires, 0, mb_strpos($expires, ';'));
3439
        } else {
3440
            $expires = '';
3441
        }
3442
3443
        $cookie_param = 'path=';
3444
        $start        = mb_strpos($cookie_str, $cookie_param);
3445
        if ($start > 0) {
3446
            $path = mb_substr($cookie_str, $start + mb_strlen($cookie_param));
3447
            $path = mb_substr($path, 0, mb_strpos($path, ';'));
3448
        } else {
3449
            $path = '/';
3450
        }
3451
3452
        $cookie_param = ';secure;';
3453
        $secure       = false;
3454
        if (false !== mb_strpos($cookie_str, $cookie_param)) {
3455
            $secure = true;
3456
        }
3457
3458
        $sep_pos = mb_strpos($value_str, '=');
3459
        if ($sep_pos) {
3460
            $name   = mb_substr($value_str, 0, $sep_pos);
3461
            $value  = mb_substr($value_str, $sep_pos + 1);
3462
            $cookie = [
3463
                'name'    => $name,
3464
                'value'   => $value,
3465
                'domain'  => $domain,
3466
                'path'    => $path,
3467
                'expires' => $expires,
3468
                'secure'  => $secure,
3469
            ];
3470
3471
            return $cookie;
3472
        }
3473
3474
        return false;
3475
    }
3476
3477
    /**
3478
     * Sort out cookies for the current request
3479
     *
3480
     * @param    array|null $cookies array with all cookies
3481
     * @param    bool       $secure  is the send-content secure or not?
3482
     * @return    string for Cookie-HTTP-Header
3483
     */
3484
    private function getCookiesForRequest($cookies, $secure = false)
3485
    {
3486
        $cookie_str = '';
3487
        if ((null !== $cookies) && is_array($cookies)) {
3488
            foreach ($cookies as $cookie) {
3489
                if (!is_array($cookie)) {
3490
                    continue;
3491
                }
3492
                $this->debug('check cookie for validity: ' . $cookie['name'] . '=' . $cookie['value']);
3493
                if (isset($cookie['expires']) && !empty($cookie['expires'])) {
3494
                    if (strtotime($cookie['expires']) <= time()) {
3495
                        $this->debug('cookie has expired');
3496
                        continue;
3497
                    }
3498
                }
3499
                if (isset($cookie['domain']) && !empty($cookie['domain'])) {
3500
                    $domain = preg_quote($cookie['domain']);
3501
                    if (!preg_match("'.*$domain$'i", $this->host)) {
3502
                        $this->debug('cookie has different domain');
3503
                        continue;
3504
                    }
3505
                }
3506
                if (isset($cookie['path']) && !empty($cookie['path'])) {
3507
                    $path = preg_quote($cookie['path']);
3508
                    if (!preg_match("'^$path.*'i", $this->path)) {
3509
                        $this->debug('cookie is for a different path');
3510
                        continue;
3511
                    }
3512
                }
3513
                if ((!$secure) && !empty($cookie['secure'])) {
3514
                    $this->debug('cookie is secure, transport is not');
3515
                    continue;
3516
                }
3517
                $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3518
                $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3519
            }
3520
        }
3521
3522
        return $cookie_str;
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
 */
3533
class Nusoap_server extends Nusoap_base
3534
{
3535
    /**
3536
     * HTTP headers of request
3537
     *
3538
     * @var array
3539
     */
3540
    private $headers = [];
3541
    /**
3542
     * HTTP request
3543
     *
3544
     * @var string
3545
     */
3546
    private $request = '';
3547
    /**
3548
     * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3549
     *
3550
     * @var string
3551
     */
3552
    public $requestHeaders = '';
3553
    /**
3554
     * SOAP Headers from request (parsed)
3555
     *
3556
     * @var mixed
3557
     */
3558
    public $requestHeader;
3559
    /**
3560
     * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3561
     *
3562
     * @var string
3563
     */
3564
    public $document = '';
3565
    /**
3566
     * SOAP payload for request (text)
3567
     *
3568
     * @var string
3569
     */
3570
    public $requestSOAP = '';
3571
    /**
3572
     * Requested method namespace URI
3573
     *
3574
     * @var string
3575
     */
3576
    private $methodURI = '';
3577
    /**
3578
     * Name of method requested
3579
     *
3580
     * @var string
3581
     */
3582
    private $methodname = '';
3583
    /**
3584
     * Method parameters from request
3585
     *
3586
     * @var array
3587
     */
3588
    private $methodparams = [];
3589
    /**
3590
     * SOAP Action from request
3591
     *
3592
     * @var string
3593
     */
3594
    private $SOAPAction = '';
3595
    /**
3596
     * Character set encoding of incoming (request) messages
3597
     *
3598
     * @var string
3599
     */
3600
    public $xml_encoding = '';
3601
    /**
3602
     * Toggles whether the parser decodes element content w/ utf8_decode()
3603
     *
3604
     * @var bool
3605
     */
3606
    public $decode_utf8 = true;
3607
    /**
3608
     * HTTP headers of response
3609
     *
3610
     * @var array
3611
     */
3612
    public $outgoing_headers = [];
3613
    /**
3614
     * HTTP response
3615
     *
3616
     * @var string
3617
     */
3618
    private $response = '';
3619
    /**
3620
     * SOAP headers for response (text or array of Soapval or associative array)
3621
     *
3622
     * @var mixed
3623
     */
3624
    public $responseHeaders = '';
3625
    /**
3626
     * SOAP payload for response (text)
3627
     *
3628
     * @var string
3629
     */
3630
    private $responseSOAP = '';
3631
    /**
3632
     * Method return value to place in response
3633
     *
3634
     * @var mixed
3635
     */
3636
    private $methodreturn = false;
3637
    /**
3638
     * Whether $methodreturn is a string of literal XML
3639
     *
3640
     * @var bool
3641
     */
3642
    public $methodreturnisliteralxml = false;
3643
    /**
3644
     * SOAP fault for response (or false)
3645
     *
3646
     * @var mixed
3647
     */
3648
    private $fault = false;
3649
    /**
3650
     * Text indication of result (for debugging)
3651
     *
3652
     * @var string
3653
     */
3654
    private $result = 'successful';
3655
    /**
3656
     * Assoc array of operations => opData; operations are added by the register()
3657
     * method or by parsing an external WSDL definition
3658
     *
3659
     * @var array
3660
     */
3661
    private $operations = [];
3662
    /**
3663
     * Wsdl instance (if one)
3664
     *
3665
     * @var mixed
3666
     */
3667
    private $wsdl = false;
3668
    /**
3669
     * URL for WSDL (if one)
3670
     *
3671
     * @var mixed
3672
     */
3673
    private $externalWSDLURL = false;
3674
    /**
3675
     * Whether to append debug to response as XML comment
3676
     *
3677
     * @var bool
3678
     */
3679
    public $debug_flag = false;
3680
3681
    /**
3682
     * Constructor
3683
     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3684
     *
3685
     * @param mixed $wsdl file path or URL (string), or Wsdl instance (object)
3686
     */
3687
    public function __construct($wsdl = false)
3688
    {
3689
        parent::__construct();
3690
        // turn on debugging?
3691
        global $debug;
3692
        global $_SERVER;
3693
        if (null !== $_SERVER) {
3694
            $this->debug('_SERVER is defined:');
3695
            $this->appendDebug($this->varDump($_SERVER));
3696
        } else {
3697
            $this->debug('_SERVER is not defined.');
3698
        }
3699
3700
        if (isset($debug)) {
3701
            $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3702
            $this->debug_flag = $debug;
3703
        } elseif (isset($_SERVER['QUERY_STRING'])) {
3704
            $qs = explode('&', $_SERVER['QUERY_STRING']);
3705
            foreach ($qs as $v) {
3706
                if (0 === mb_strpos($v, 'debug=')) {
3707
                    $this->debug('In nusoap_server, set debug_flag=' . mb_substr($v, 6) . ' based on query string #1');
3708
                    $this->debug_flag = mb_substr($v, 6);
0 ignored issues
show
Documentation Bug introduced by
The property $debug_flag was declared of type boolean, but mb_substr($v, 6) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
3709
                }
3710
            }
3711
        }
3712
3713
        // Wsdl
3714
        if ($wsdl) {
3715
            $this->debug('In nusoap_server, WSDL is specified');
3716
            if (is_object($wsdl) && ('Wsdl' === get_class($wsdl))) {
3717
                $this->wsdl            = $wsdl;
3718
                $this->externalWSDLURL = $this->wsdl->wsdl;
3719
                $this->debug('Use existing Wsdl instance from ' . $this->externalWSDLURL);
3720
            } else {
3721
                $this->debug('Create Wsdl from ' . $wsdl);
3722
                $this->wsdl = new Wsdl($wsdl);
3723
                $this->externalWSDLURL = $wsdl;
3724
            }
3725
            $this->appendDebug($this->wsdl->getDebug());
3726
            $this->wsdl->clearDebug();
3727
            if (false !== ($err = $this->wsdl->getError())) {
3728
                die('WSDL ERROR: ' . $err);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
3729
            }
3730
        }
3731
    }
3732
3733
    /**
3734
     * Processes request and returns response
3735
     *
3736
     * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3737
     */
3738
    public function service($data)
3739
    {
3740
        global $_SERVER;
3741
        if (isset($_SERVER['REQUEST_METHOD'])) {
3742
            $rm = $_SERVER['REQUEST_METHOD'];
3743
        } else {
3744
            $rm = '';
3745
        }
3746
3747
        if (isset($_SERVER['QUERY_STRING'])) {
3748
            $qs = $_SERVER['QUERY_STRING'];
3749
        } else {
3750
            $qs = '';
3751
        }
3752
        $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . mb_strlen($data));
3753
        if ('POST' === $rm) {
3754
            $this->debug('In service, invoke the request');
3755
            $this->parse_request($data);
3756
            if (!$this->fault) {
3757
                $this->invoke_method();
3758
            }
3759
            if (!$this->fault) {
3760
                $this->serialize_return();
3761
            }
3762
            $this->send_response();
3763
        } elseif (preg_match('/wsdl/', $qs)) {
3764
            $this->debug('In service, this is a request for WSDL');
3765
            if ($this->externalWSDLURL) {
3766
                if (false !== mb_strpos($this->externalWSDLURL, 'http://')) {
3767
                    // assume URL
3768
                    $this->debug('In service, re-direct for WSDL');
3769
                    header('Location: ' . $this->externalWSDLURL);
3770
                } else {
3771
                    // assume file
3772
                    $this->debug('In service, use file passthru for WSDL');
3773
                    header("Content-Type: text/xml\r\n");
3774
                    $pos = mb_strpos($this->externalWSDLURL, 'file://');
3775
                    if (false === $pos) {
3776
                        $filename = $this->externalWSDLURL;
0 ignored issues
show
Unused Code introduced by
The assignment to $filename is dead and can be removed.
Loading history...
3777
                    } else {
3778
                        $filename = mb_substr($this->externalWSDLURL, $pos + 7);
3779
                    }
3780
                    $fp = fopen($this->externalWSDLURL, 'rb');
3781
                    fpassthru($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fpassthru() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3781
                    fpassthru(/** @scrutinizer ignore-type */ $fp);
Loading history...
3782
                }
3783
            } elseif ($this->wsdl) {
3784
                $this->debug('In service, serialize WSDL');
3785
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3786
                print $this->wsdl->serialize($this->debug_flag);
3787
                if ($this->debug_flag) {
3788
                    $this->debug('Wsdl:');
3789
                    $this->appendDebug($this->varDump($this->wsdl));
3790
                    print $this->getDebugAsXMLComment();
3791
                }
3792
            } else {
3793
                $this->debug('In service, there is no WSDL');
3794
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3795
                print 'This service does not provide WSDL';
3796
            }
3797
        } elseif ($this->wsdl) {
3798
            $this->debug('In service, return Web description');
3799
            print $this->wsdl->webDescription();
3800
        } else {
3801
            $this->debug('In service, no Web description');
3802
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3803
            print 'This service does not provide a Web description';
3804
        }
3805
    }
3806
3807
    /**
3808
     * Parses HTTP request headers.
3809
     *
3810
     * The following fields are set by this function (when successful)
3811
     *
3812
     * headers
3813
     * request
3814
     * xml_encoding
3815
     * SOAPAction
3816
     */
3817
    private function parse_http_headers()
3818
    {
3819
        global $_SERVER;
3820
        $this->request    = '';
3821
        $this->SOAPAction = '';
3822
        if (function_exists('getallheaders')) {
3823
            $this->debug('In parse_http_headers, use getallheaders');
3824
            $headers = getallheaders();
3825
            foreach ($headers as $k => $v) {
3826
                $k                 = mb_strtolower($k);
3827
                $this->headers[$k] = $v;
3828
                $this->request     .= "$k: $v\r\n";
3829
                $this->debug("$k: $v");
3830
            }
3831
            // get SOAPAction header
3832
            if (isset($this->headers['soapaction'])) {
3833
                $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
3834
            }
3835
            // get the character encoding of the incoming request
3836
            if (isset($this->headers['content-type']) && mb_strpos($this->headers['content-type'], '=')) {
3837
                $enc                = str_replace('"', '', mb_substr(mb_strstr($this->headers['content-type'], '='), 1));
3838
                $this->xml_encoding = 'US-ASCII';
3839
                if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3840
                    $this->xml_encoding = mb_strtoupper($enc);
3841
                }
3842
            } else {
3843
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3844
                $this->xml_encoding = 'ISO-8859-1';
3845
            }
3846
        } elseif (null !== $_SERVER && is_array($_SERVER)) {
3847
            $this->debug('In parse_http_headers, use _SERVER');
3848
            foreach ($_SERVER as $k => $v) {
3849
                if (0 === mb_strpos($k, 'HTTP_')) {
3850
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', mb_substr($k, 5))));
3851
                } else {
3852
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', $k)));
3853
                }
3854
                if ('soapaction' === $k) {
3855
                    // get SOAPAction header
3856
                    $k                = 'SOAPAction';
3857
                    $v                = str_replace('"', '', $v);
3858
                    $v                = str_replace('\\', '', $v);
3859
                    $this->SOAPAction = $v;
3860
                } elseif ('content-type' === $k) {
3861
                    // get the character encoding of the incoming request
3862
                    if (mb_strpos($v, '=')) {
3863
                        $enc                = mb_substr(mb_strstr($v, '='), 1);
3864
                        $enc                = str_replace('"', '', $enc);
3865
                        $enc                = str_replace('\\', '', $enc);
3866
                        $this->xml_encoding = 'US-ASCII';
3867
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3868
                            $this->xml_encoding = mb_strtoupper($enc);
3869
                        }
3870
                    } else {
3871
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3872
                        $this->xml_encoding = 'ISO-8859-1';
3873
                    }
3874
                }
3875
                $this->headers[$k] = $v;
3876
                $this->request     .= "$k: $v\r\n";
3877
                $this->debug("$k: $v");
3878
            }
3879
        } else {
3880
            $this->debug('In parse_http_headers, HTTP headers not accessible');
3881
            $this->setError('HTTP headers not accessible');
3882
        }
3883
    }
3884
3885
    /**
3886
     * Parses a request
3887
     *
3888
     * The following fields are set by this function (when successful)
3889
     *
3890
     * headers
3891
     * request
3892
     * xml_encoding
3893
     * SOAPAction
3894
     * request
3895
     * requestSOAP
3896
     * methodURI
3897
     * methodname
3898
     * methodparams
3899
     * requestHeaders
3900
     * document
3901
     *
3902
     * This sets the fault field on error
3903
     *
3904
     * @param    string $data XML string
3905
     */
3906
    private function parse_request($data = '')
3907
    {
3908
        $this->debug('entering parse_request()');
3909
        $this->parse_http_headers();
3910
        $this->debug('got character encoding: ' . $this->xml_encoding);
3911
        // uncompress if necessary
3912
        if (isset($this->headers['content-encoding']) && '' !== $this->headers['content-encoding']) {
3913
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3914
            if ('deflate' === $this->headers['content-encoding'] || 'gzip' === $this->headers['content-encoding']) {
3915
                // if decoding works, use it. else assume data wasn't gzencoded
3916
                if (function_exists('gzuncompress')) {
3917
                    if ('deflate' === $this->headers['content-encoding'] && $degzdata = @gzuncompress($data)) {
3918
                        $data = $degzdata;
3919
                    } elseif ('gzip' === $this->headers['content-encoding'] && $degzdata = gzinflate(mb_substr($data, 10))) {
3920
                        $data = $degzdata;
3921
                    } else {
3922
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3923
3924
                        return;
3925
                    }
3926
                } else {
3927
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3928
3929
                    return;
3930
                }
3931
            }
3932
        }
3933
        $this->request     .= "\r\n" . $data;
3934
        $data              = $this->parseRequest($this->headers, $data);
3935
        $this->requestSOAP = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data can also be of type false. However, the property $requestSOAP is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
3936
        $this->debug('leaving parse_request');
3937
    }
3938
3939
    /**
3940
     * Invokes a PHP function for the requested SOAP method
3941
     *
3942
     * The following fields are set by this function (when successful)
3943
     *
3944
     * methodreturn
3945
     *
3946
     * Note that the PHP function that is called may also set the following
3947
     * fields to affect the response sent to the client
3948
     *
3949
     * responseHeaders
3950
     * outgoing_headers
3951
     *
3952
     * This sets the fault field on error
3953
     */
3954
    private function invoke_method()
3955
    {
3956
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3957
        //
3958
        // if you are debugging in this area of the code, your service uses a class to implement methods,
3959
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
3960
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
3961
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
3962
        // the XML request and reading the XML response.  you need to add the RequestElementName and
3963
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
3964
        // generates for the method.  these parameters are used to specify the correct XML element names
3965
        // for .NET to use, i.e. the names with the '.' in them.
3966
        //
3967
        $orig_methodname = $this->methodname;
3968
        if ($this->wsdl) {
3969
            if (false !== ($this->opData = $this->wsdl->getOperationData($this->methodname))) {
0 ignored issues
show
Bug Best Practice introduced by
The property opData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
3970
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3971
                $this->appendDebug('opData=' . $this->varDump($this->opData));
3972
            } elseif (false !== ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction))) {
3973
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3974
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3975
                $this->appendDebug('opData=' . $this->varDump($this->opData));
3976
                $this->methodname = $this->opData['name'];
3977
            } else {
3978
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3979
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3980
3981
                return;
3982
            }
3983
        } else {
3984
            $this->debug('in invoke_method, no WSDL to validate method');
3985
        }
3986
3987
        // if a . is present in $this->methodname, we see if there is a class in scope,
3988
        // which could be referred to. We will also distinguish between two deliminators,
3989
        // to allow methods to be called a the class or an instance
3990
        $delim = '';
3991
        if (mb_strpos($this->methodname, '..') > 0) {
3992
            $delim = '..';
3993
        } elseif (mb_strpos($this->methodname, '.') > 0) {
3994
            $delim = '.';
3995
        }
3996
        $this->debug("in invoke_method, delim=$delim");
3997
        $class  = '';
3998
        $method = '';
3999
        if (mb_strlen($delim) > 0 && 1 === mb_substr_count($this->methodname, $delim)) {
4000
            $try_class = mb_substr($this->methodname, 0, mb_strpos($this->methodname, $delim));
4001
            if (class_exists($try_class)) {
4002
                // get the class and method name
4003
                $class  = $try_class;
4004
                $method = mb_substr($this->methodname, mb_strpos($this->methodname, $delim) + mb_strlen($delim));
4005
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4006
            } else {
4007
                $this->debug("in invoke_method, class=$try_class not found");
4008
            }
4009
        } elseif (mb_strlen($delim) > 0 && mb_substr_count($this->methodname, $delim) > 1) {
4010
            $split  = explode($delim, $this->methodname);
4011
            $method = array_pop($split);
4012
            $class  = implode('\\', $split);
4013
        } else {
4014
            $try_class = '';
4015
            $this->debug('in invoke_method, no class to try');
4016
        }
4017
4018
        // does method exist?
4019
        if ('' === $class) {
4020
            if (!function_exists($this->methodname)) {
4021
                $this->debug("in invoke_method, function '$this->methodname' not found!");
4022
                $this->result = 'fault: method not found';
4023
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $try_class does not seem to be defined for all execution paths leading up to this point.
Loading history...
4024
4025
                return;
4026
            }
4027
        } else {
4028
            $method_to_compare = (0 === mb_strpos(PHP_VERSION, '4.')) ? mb_strtolower($method) : $method;
4029
            if (!in_array($method_to_compare, get_class_methods($class), true)) {
4030
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4031
                $this->result = 'fault: method not found';
4032
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4033
4034
                return;
4035
            }
4036
        }
4037
4038
        // evaluate message, getting back parameters
4039
        // verify that request parameters match the method's signature
4040
        if (!$this->verify_method($this->methodname, $this->methodparams)) {
4041
            // debug
4042
            $this->debug('ERROR: request not verified against method signature');
4043
            $this->result = 'fault: request failed validation against method signature';
4044
            // return fault
4045
            $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
4046
4047
            return;
4048
        }
4049
4050
        // if there are parameters to pass
4051
        $this->debug('in invoke_method, params:');
4052
        $this->appendDebug($this->varDump($this->methodparams));
4053
        $this->debug("in invoke_method, calling '$this->methodname'");
4054
        if (!function_exists('call_user_func_array')) {
4055
            if ('' === $class) {
4056
                $this->debug('in invoke_method, calling function using eval()');
4057
                $funcCall = "\$this->methodreturn = $this->methodname(";
4058
            } else {
4059
                if ('..' === $delim) {
4060
                    $this->debug('in invoke_method, calling class method using eval()');
4061
                    $funcCall = '$this->methodreturn = ' . $class . '::' . $method . '(';
4062
                } else {
4063
                    $this->debug('in invoke_method, calling instance method using eval()');
4064
                    // generate unique instance name
4065
                    $instname = '$inst_' . time();
4066
                    $funcCall = $instname . ' = new ' . $class . '(); ';
4067
                    $funcCall .= '$this->methodreturn = ' . $instname . '->' . $method . '(';
4068
                }
4069
            }
4070
            if ($this->methodparams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->methodparams of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
4071
                foreach ($this->methodparams as $param) {
4072
                    if (is_array($param) || is_object($param)) {
4073
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4074
4075
                        return;
4076
                    }
4077
                    $funcCall .= "\"$param\",";
4078
                }
4079
                $funcCall = mb_substr($funcCall, 0, -1);
4080
            }
4081
            $funcCall .= ');';
4082
            $this->debug('in invoke_method, function call: ' . $funcCall);
4083
            @eval($funcCall);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
4084
        } else {
4085
            if ('' === $class) {
4086
                $this->debug('in invoke_method, calling function using call_user_func_array()');
4087
                $call_arg = $this->methodname;
4088
            // straight assignment changes $this->methodname to lower case after call_user_func_array()
4089
            } elseif ('..' === $delim) {
4090
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
4091
                $call_arg = [$class, $method];
4092
            } else {
4093
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4094
                $instance = new $class();
4095
                $call_arg = [&$instance, $method];
4096
            }
4097
            if (is_array($this->methodparams) && count($this->methodparams) > 0) {
4098
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4099
            } else {
4100
                $this->methodreturn = call_user_func_array($call_arg, []);
4101
            }
4102
        }
4103
        $this->debug('in invoke_method, methodreturn:');
4104
        $this->appendDebug($this->varDump($this->methodreturn));
4105
        $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
4106
    }
4107
4108
    /**
4109
     * Serializes the return value from a PHP function into a full SOAP Envelope
4110
     *
4111
     * The following fields are set by this function (when successful)
4112
     *
4113
     * responseSOAP
4114
     *
4115
     * This sets the fault field on error
4116
     */
4117
    private function serialize_return()
4118
    {
4119
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4120
        // if fault
4121
        if (isset($this->methodreturn) && is_object($this->methodreturn) && (('soap_fault' === get_class($this->methodreturn)) || ('Nusoap_fault' === get_class($this->methodreturn)))) {
4122
            $this->debug('got a fault object from method');
4123
            $this->fault = $this->methodreturn;
4124
4125
            return;
4126
        }
4127
4128
        if ($this->methodreturnisliteralxml) {
4129
            $return_val = $this->methodreturn;
4130
        // returned value(s)
4131
        } else {
4132
            $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
4133
            $this->debug('serializing return value');
4134
            if ($this->wsdl) {
4135
                if (is_array($this->opData['output']['parts']) && count($this->opData['output']['parts']) > 1) {
4136
                    $this->debug('more than one output part, so use the method return unchanged');
4137
                    $opParams = $this->methodreturn;
4138
                } elseif (1 === count($this->opData['output']['parts'])) {
4139
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
4140
                    // TODO: verify that it is not already wrapped!
4141
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
4142
                    //	$this->debug('wrap in element named ' . $name);
4143
                    //}
4144
                    $opParams = [$this->methodreturn];
4145
                }
4146
                $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $opParams does not seem to be defined for all execution paths leading up to this point.
Loading history...
4147
                $this->appendDebug($this->wsdl->getDebug());
4148
                $this->wsdl->clearDebug();
4149
                if (false !== ($errstr = $this->wsdl->getError())) {
4150
                    $this->debug('got Wsdl error: ' . $errstr);
4151
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4152
4153
                    return;
4154
                }
4155
            } else {
4156
                if (isset($this->methodreturn)) {
4157
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
4158
                } else {
4159
                    $return_val = '';
4160
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
4161
                }
4162
            }
4163
        }
4164
        $this->debug('return value:');
4165
        $this->appendDebug($this->varDump($return_val));
4166
        $this->debug('serializing response');
4167
        if ($this->wsdl) {
4168
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4169
            if ('rpc' === $this->opData['style']) {
4170
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4171
                if ('literal' === $this->opData['output']['use']) {
4172
                    // 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
4173
                    if ($this->methodURI) {
4174
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4175
                    } else {
4176
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4177
                    }
4178
                } else {
4179
                    if ($this->methodURI) {
4180
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4181
                    } else {
4182
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4183
                    }
4184
                }
4185
            } else {
4186
                $this->debug('style is not rpc for serialization: assume document');
4187
                $payload = $return_val;
4188
            }
4189
        } else {
4190
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4191
            $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4192
        }
4193
        $this->result = 'successful';
4194
        if ($this->wsdl) {
4195
            //if($this->debug_flag){
4196
            $this->appendDebug($this->wsdl->getDebug());
4197
            //	}
4198
            $encodingStyle = '';
4199
            if (isset($this->opData['output']['encodingStyle'])) {
4200
                $encodingStyle = $this->opData['output']['encodingStyle'];
4201
            }
4202
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4203
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
4204
        } else {
4205
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
4206
        }
4207
        $this->debug('Leaving serialize_return');
4208
    }
4209
4210
    /**
4211
     * Sends an HTTP response
4212
     *
4213
     * The following fields are set by this function (when successful)
4214
     *
4215
     * outgoing_headers
4216
     * response
4217
     */
4218
    private function send_response()
4219
    {
4220
        $this->debug('Enter send_response');
4221
        if ($this->fault) {
4222
            $payload                  = $this->fault->serialize();
4223
            $this->outgoing_headers[] = 'HTTP/1.0 500 Internal Server Error';
4224
            $this->outgoing_headers[] = 'Status: 500 Internal Server Error';
4225
        } else {
4226
            $payload = $this->responseSOAP;
4227
            // Some combinations of PHP+Web server allow the Status
4228
            // to come through as a header.  Since OK is the default
4229
            // just do nothing.
4230
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4231
            // $this->outgoing_headers[] = "Status: 200 OK";
4232
        }
4233
        // add debug data if in debug mode
4234
        if (!empty($this->debug_flag)) {
4235
            $payload .= $this->getDebugAsXMLComment();
4236
        }
4237
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4238
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4239
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . $rev[1] . ')';
4240
        // Let the Web server decide about this
4241
        //$this->outgoing_headers[] = "Connection: Close\r\n";
4242
        $payload                  = $this->getHTTPBody($payload);
4243
        $type                     = $this->getHTTPContentType();
4244
        $charset                  = $this->getHTTPContentTypeCharset();
4245
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4246
        //begin code to compress payload - by John
4247
        // NOTE: there is no way to know whether the Web server will also compress
4248
        // this data.
4249
        if (mb_strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4250
            if (false !== mb_strpos($this->headers['accept-encoding'], 'gzip')) {
4251
                if (function_exists('gzencode')) {
4252
                    if (!empty($this->debug_flag)) {
4253
                        $payload .= '<!-- Content being gzipped -->';
4254
                    }
4255
                    $this->outgoing_headers[] = 'Content-Encoding: gzip';
4256
                    $payload                  = gzencode($payload);
4257
                } else {
4258
                    if (!empty($this->debug_flag)) {
4259
                        $payload .= '<!-- Content will not be gzipped: no gzencode -->';
4260
                    }
4261
                }
4262
            } elseif (false !== mb_strpos($this->headers['accept-encoding'], 'deflate')) {
4263
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4264
                // instead of gzcompress output,
4265
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4266
                if (function_exists('gzdeflate')) {
4267
                    if (!empty($this->debug_flag)) {
4268
                        $payload .= '<!-- Content being deflated -->';
4269
                    }
4270
                    $this->outgoing_headers[] = 'Content-Encoding: deflate';
4271
                    $payload                  = gzdeflate($payload);
4272
                } else {
4273
                    if (!empty($this->debug_flag)) {
4274
                        $payload .= '<!-- Content will not be deflated: no gzcompress -->';
4275
                    }
4276
                }
4277
            }
4278
        }
4279
        //end code
4280
        $this->outgoing_headers[] = 'Content-Length: ' . mb_strlen($payload);
4281
        reset($this->outgoing_headers);
4282
        foreach ($this->outgoing_headers as $hdr) {
4283
            header($hdr, false);
4284
        }
4285
        print $payload;
4286
        $this->response = implode("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
4287
    }
4288
4289
    /**
4290
     * Takes the value that was created by parsing the request
4291
     * and compares to the method's signature, if available.
4292
     *
4293
     * @param    string $operation The operation to be invoked
4294
     * @param    array  $request   The array of parameter values
4295
     * @return bool    Whether the operation was found
4296
     */
4297
    private function verify_method($operation, $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

4297
    private function verify_method($operation, /** @scrutinizer ignore-unused */ $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
4298
    {
4299
        if (isset($this->wsdl) && is_object($this->wsdl)) {
4300
            if ($this->wsdl->getOperationData($operation)) {
4301
                return true;
4302
            }
4303
        } elseif (isset($this->operations[$operation])) {
4304
            return true;
4305
        }
4306
4307
        return false;
4308
    }
4309
4310
    /**
4311
     * Processes SOAP message received from client
4312
     *
4313
     * @param    array  $headers The HTTP headers
4314
     * @param    string $data    unprocessed request data from client
4315
     * @return    mixed    value of the message, decoded into a PHP type
4316
     */
4317
    private function parseRequest($headers, $data)
4318
    {
4319
        $this->debug('Entering parseRequest() for data of length ' . mb_strlen($data) . ' headers:');
4320
        $this->appendDebug($this->varDump($headers));
4321
        if (!isset($headers['content-type'])) {
4322
            $this->setError('Request not of type text/xml (no content-type header)');
4323
4324
            return false;
4325
        }
4326
        if (false === mb_strpos($headers['content-type'], 'text/xml')) {
4327
            $this->setError('Request not of type text/xml');
4328
4329
            return false;
4330
        }
4331
        if (mb_strpos($headers['content-type'], '=')) {
4332
            $enc = str_replace('"', '', mb_substr(mb_strstr($headers['content-type'], '='), 1));
4333
            $this->debug('Got response encoding: ' . $enc);
4334
            $this->xml_encoding = 'US-ASCII';
4335
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
4336
                $this->xml_encoding = mb_strtoupper($enc);
4337
            }
4338
        } else {
4339
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4340
            $this->xml_encoding = 'ISO-8859-1';
4341
        }
4342
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4343
        // parse response, get soap parser obj
4344
        $parser = new Nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
4345
        // parser debug
4346
        $this->debug("parser debug: \n" . $parser->getDebug());
4347
        // if fault occurred during message parsing
4348
        if (false !== ($err = $parser->getError())) {
4349
            $this->result = 'fault: error in msg parsing: ' . $err;
4350
            $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
4351
        // else successfully parsed request into Soapval object
4352
        } else {
4353
            // get/set methodname
4354
            $this->methodURI  = $parser->root_struct_namespace;
4355
            $this->methodname = $parser->root_struct_name;
4356
            $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4357
            $this->debug('calling parser->get_soapbody()');
4358
            $this->methodparams = $parser->get_soapbody();
4359
            // get SOAP headers
4360
            $this->requestHeaders = $parser->getHeaders();
4361
            // get SOAP Header
4362
            $this->requestHeader = $parser->get_soapheader();
4363
            // add document for doclit support
4364
            $this->document = $parser->document;
4365
        }
4366
    }
4367
4368
    /**
4369
     * Gets the HTTP body for the current response.
4370
     *
4371
     * @param string $soapmsg The SOAP payload
4372
     * @return string The HTTP body, which includes the SOAP payload
4373
     */
4374
    private function getHTTPBody($soapmsg)
4375
    {
4376
        return $soapmsg;
4377
    }
4378
4379
    /**
4380
     * Gets the HTTP content type for the current response.
4381
     *
4382
     * Note: getHTTPBody must be called before this.
4383
     *
4384
     * @return string the HTTP content type for the current response.
4385
     */
4386
    private function getHTTPContentType()
4387
    {
4388
        return 'text/xml';
4389
    }
4390
4391
    /**
4392
     * Gets the HTTP content type charset for the current response.
4393
     * Returns false for non-text content types.
4394
     *
4395
     * Note: getHTTPBody must be called before this.
4396
     *
4397
     * @return string the HTTP content type charset for the current response.
4398
     */
4399
    private function getHTTPContentTypeCharset()
4400
    {
4401
        return $this->soap_defencoding;
4402
    }
4403
4404
    /**
4405
     * Add a method to the dispatch map (this has been replaced by the register method)
4406
     *
4407
     * @param    string $methodname
4408
     * @param    string $in  array of input values
4409
     * @param    string $out array of output values
4410
     * @deprecated
4411
     */
4412
    public function add_to_map($methodname, $in, $out)
4413
    {
4414
        $this->operations[$methodname] = ['name' => $methodname, 'in' => $in, 'out' => $out];
4415
    }
4416
4417
    /**
4418
     * Register a service function with the server
4419
     *
4420
     * @param    string $name          the name of the PHP function, class.method or class..method
4421
     * @param    array  $in            assoc array of input values: key = param name, value = param type
4422
     * @param    array  $out           assoc array of output values: key = param name, value = param type
4423
     * @param    mixed  $namespace     the element namespace for the method or false
4424
     * @param    mixed  $soapaction    the soapaction for the method or false
4425
     * @param    mixed  $style         optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4426
     * @param    mixed  $use           optional (encoded|literal) or false
4427
     * @param    string $documentation optional Description to include in WSDL
4428
     * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4429
     * @return bool
4430
     */
4431
    public function register($name, $in = [], $out = [], $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '')
4432
    {
4433
        global $_SERVER;
4434
        if ($this->externalWSDLURL) {
4435
            die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
4436
        }
4437
        if (!$name) {
4438
            die('You must specify a name when you register an operation');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
4439
        }
4440
        if (!is_array($in)) {
0 ignored issues
show
introduced by
The condition is_array($in) is always true.
Loading history...
4441
            die('You must provide an array for operation inputs');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
4442
        }
4443
        if (!is_array($out)) {
0 ignored issues
show
introduced by
The condition is_array($out) is always true.
Loading history...
4444
            die('You must provide an array for operation outputs');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
4445
        }
4446
        if (false === $namespace) {
4447
        }
4448
        if (false === $soapaction) {
4449
            if (null !== $_SERVER) {
4450
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
4451
                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4452
                $HTTPS = isset($_SERVER['HTTPS']) ? : 'off';
4453
            } else {
4454
                $this->setError(' _SERVER  is not available');
4455
            }
4456
            $SCHEME = 'http';
4457
            if ('1' === $HTTPS || 'on' === $HTTPS) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $HTTPS does not seem to be defined for all execution paths leading up to this point.
Loading history...
4458
                $SCHEME = 'https';
4459
            }
4460
            $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SERVER_NAME does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $SCRIPT_NAME does not seem to be defined for all execution paths leading up to this point.
Loading history...
4461
        }
4462
        if (false === $style) {
4463
            $style = 'rpc';
4464
        }
4465
        if (false === $use) {
4466
            $use = 'encoded';
4467
        }
4468
        if ('encoded' === $use && '' === $encodingStyle) {
4469
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4470
        }
4471
4472
        $this->operations[$name] = [
4473
            'name'       => $name,
4474
            'in'         => $in,
4475
            'out'        => $out,
4476
            'namespace'  => $namespace,
4477
            'soapaction' => $soapaction,
4478
            'style'      => $style,
4479
        ];
4480
        if ($this->wsdl) {
4481
            $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
4482
        }
4483
4484
        return true;
4485
    }
4486
4487
    /**
4488
     * Specify a fault to be returned to the client.
4489
     * This also acts as a flag to the server that a fault has occured.
4490
     *
4491
     * @param    string $faultcode
4492
     * @param    string $faultstring
4493
     * @param    string $faultactor
4494
     * @param    string $faultdetail
4495
     */
4496
    public function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
4497
    {
4498
        if ('' === $faultdetail && $this->debug_flag) {
4499
            $faultdetail = $this->getDebug();
4500
        }
4501
        $this->fault                   = new Nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
4502
        $this->fault->soap_defencoding = $this->soap_defencoding;
4503
    }
4504
4505
    /**
4506
     * Sets up Wsdl object.
4507
     * Acts as a flag to enable internal WSDL generation
4508
     *
4509
     * @param string $serviceName           , name of the service
4510
     * @param mixed  $namespace             optional 'tns' service namespace or false
4511
     * @param mixed  $endpoint              optional URL of service endpoint or false
4512
     * @param string $style                 optional (rpc|document) WSDL style (also specified by operation)
4513
     * @param string $transport             optional SOAP transport
4514
     * @param mixed  $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4515
     */
4516
    public function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4517
    {
4518
        global $_SERVER;
4519
4520
        if (null !== $_SERVER) {
4521
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
4522
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
4523
            $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4524
            $HTTPS = isset($_SERVER['HTTPS']) ?: 'off';
4525
        } else {
4526
            $this->setError(' $_SERVER is not available');
4527
        }
4528
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4529
        $colon = mb_strpos($SERVER_NAME, ':');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SERVER_NAME does not seem to be defined for all execution paths leading up to this point.
Loading history...
4530
        if ($colon) {
4531
            $SERVER_NAME = mb_substr($SERVER_NAME, 0, $colon);
4532
        }
4533
        if (80 === $SERVER_PORT) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SERVER_PORT does not seem to be defined for all execution paths leading up to this point.
Loading history...
4534
            $SERVER_PORT = '';
4535
        } else {
4536
            $SERVER_PORT = ':' . $SERVER_PORT;
4537
        }
4538
        if (false === $namespace) {
4539
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4540
        }
4541
4542
        if (false === $endpoint) {
4543
            $SCHEME = 'http';
4544
            if ('1' === $HTTPS || 'on' === $HTTPS) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $HTTPS does not seem to be defined for all execution paths leading up to this point.
Loading history...
4545
                $SCHEME = 'https';
4546
            }
4547
            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SCRIPT_NAME does not seem to be defined for all execution paths leading up to this point.
Loading history...
4548
        }
4549
4550
        if (false === $schemaTargetNamespace) {
4551
            $schemaTargetNamespace = $namespace;
4552
        }
4553
4554
        $this->wsdl = new Wsdl();
4555
        $this->wsdl->serviceName        = $serviceName;
0 ignored issues
show
Bug Best Practice introduced by
The property serviceName does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
4556
        $this->wsdl->endpoint           = $endpoint;
4557
        $this->wsdl->namespaces['tns']  = $namespace;
4558
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4559
        $this->wsdl->namespaces['Wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4560
        if ($schemaTargetNamespace !== $namespace) {
4561
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4562
        }
4563
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new Nusoap_xmlschema('', '', $this->wsdl->namespaces);
4564
        if ('document' === $style) {
4565
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4566
        }
4567
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace                                   = $schemaTargetNamespace;
4568
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = ['location' => '', 'loaded' => true];
4569
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0]          = ['location' => '', 'loaded' => true];
4570
        $this->wsdl->bindings[$serviceName . 'Binding']                                                          = [
4571
            'name'      => $serviceName . 'Binding',
4572
            'style'     => $style,
4573
            'transport' => $transport,
4574
            'portType'  => $serviceName . 'PortType',
4575
        ];
4576
        $this->wsdl->ports[$serviceName . 'Port']                                                                = [
4577
            'binding'     => $serviceName . 'Binding',
4578
            'location'    => $endpoint,
4579
            'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/',
4580
        ];
4581
    }
4582
}
4583
4584
/**
4585
 * Backward compatibility
4586
 */
4587
class Soap_server extends Nusoap_server
4588
{
4589
}
4590
4591
/**
4592
 * Parses a WSDL file, allows access to it's data, other utility methods.
4593
 * also builds WSDL structures programmatically.
4594
 *
4595
 * @author   Dietrich Ayala <[email protected]>
4596
 * @author   Scott Nichol <[email protected]>
4597
 */
4598
class Wsdl extends Nusoap_base
4599
{
4600
    // URL or filename of the root of this WSDL
4601
    public $wsdl;
4602
    // define internal arrays of bindings, ports, operations, messages, etc.
4603
    public $schemas       = [];
4604
    public $currentSchema;
4605
    public $message       = [];
4606
    public $complexTypes  = [];
4607
    public $messages      = [];
4608
    public $currentMessage;
4609
    public $currentOperation;
4610
    public $portTypes     = [];
4611
    public $currentPortType;
4612
    public $bindings      = [];
4613
    public $currentBinding;
4614
    public $ports         = [];
4615
    public $currentPort;
4616
    public $opData        = [];
4617
    public $status        = '';
4618
    public $documentation = false;
4619
    public $endpoint      = '';
4620
    // array of Wsdl docs to import
4621
    public $import = [];
4622
    // parser vars
4623
    public $parser;
4624
    public $position    = 0;
4625
    public $depth       = 0;
4626
    public $depth_array = [];
4627
    // for getting Wsdl
4628
    public $proxyhost        = '';
4629
    public $proxyport        = '';
4630
    public $proxyusername    = '';
4631
    public $proxypassword    = '';
4632
    public $timeout          = 0;
4633
    public $response_timeout = 30;
4634
    public $curl_options     = [];    // User-specified cURL options
4635
    public $use_curl         = false;            // whether to always try to use cURL
4636
    // for HTTP authentication
4637
    public $username    = '';                // Username for HTTP authentication
4638
    public $password    = '';                // Password for HTTP authentication
4639
    public $authtype    = '';                // Type of HTTP authentication
4640
    public $certRequest = [];        // Certificate for HTTP SSL authentication
4641
4642
    /**
4643
     * Constructor
4644
     *
4645
     * @param string      $wsdl             WSDL document URL
4646
     * @param bool|string $proxyhost
4647
     * @param bool|string $proxyport
4648
     * @param bool|string $proxyusername
4649
     * @param bool|string $proxypassword
4650
     * @param int         $timeout          set the connection timeout
4651
     * @param int         $response_timeout set the response timeout
4652
     * @param array       $curl_options     user-specified cURL options
4653
     * @param bool        $use_curl         try to use cURL
4654
     */
4655
    public function __construct($wsdl = '', $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $curl_options = null, $use_curl = false)
4656
    {
4657
        parent::__construct();
4658
        $this->debug("ctor Wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4659
        $this->proxyhost        = $proxyhost;
4660
        $this->proxyport        = $proxyport;
4661
        $this->proxyusername    = $proxyusername;
4662
        $this->proxypassword    = $proxypassword;
4663
        $this->timeout          = $timeout;
4664
        $this->response_timeout = $response_timeout;
4665
        if (is_array($curl_options)) {
4666
            $this->curl_options = $curl_options;
4667
        }
4668
        $this->use_curl = $use_curl;
4669
        $this->fetchWSDL($wsdl);
4670
    }
4671
4672
    /**
4673
     * Fetches the WSDL document and parses it
4674
     *
4675
     * @param $wsdl
4676
     */
4677
    public function fetchWSDL($wsdl)
4678
    {
4679
        $this->debug("parse and process WSDL path=$wsdl");
4680
        $this->wsdl = $wsdl;
4681
        // parse Wsdl file
4682
        if ('' !== $this->wsdl) {
4683
            $this->parseWSDL($this->wsdl);
4684
        }
4685
        // imports
4686
        // TODO: handle imports more properly, grabbing them in-line and nesting them
4687
        $imported_urls = [];
4688
        $imported      = 1;
4689
        while ($imported > 0) {
4690
            $imported = 0;
4691
            // Schema imports
4692
            foreach ($this->schemas as $ns => $list) {
4693
                foreach ($list as $xs) {
4694
                    $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4695
                    foreach ($xs->imports as $ns2 => $list2) {
4696
                        for ($ii = 0, $iiMax = count($list2); $ii < $iiMax; ++$ii) {
4697
                            if (!$list2[$ii]['loaded']) {
4698
                                $this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;
4699
                                $url                                                    = $list2[$ii]['location'];
4700
                                if ('' !== $url) {
4701
                                    $urlparts = parse_url($url);
4702
                                    if (!isset($urlparts['host'])) {
4703
                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . mb_substr($wsdlparts['path'], 0, mb_strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4704
                                    }
4705
                                    if (!in_array($url, $imported_urls, true)) {
4706
                                        $this->parseWSDL($url);
4707
                                        ++$imported;
4708
                                        $imported_urls[] = $url;
4709
                                    }
4710
                                } else {
4711
                                    $this->debug('Unexpected scenario: empty URL for unloaded import');
4712
                                }
4713
                            }
4714
                        }
4715
                    }
4716
                }
4717
            }
4718
            // WSDL imports
4719
            $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4720
            foreach ($this->import as $ns => $list) {
4721
                for ($ii = 0, $iiMax = count($list); $ii < $iiMax; ++$ii) {
4722
                    if (!$list[$ii]['loaded']) {
4723
                        $this->import[$ns][$ii]['loaded'] = true;
4724
                        $url                              = $list[$ii]['location'];
4725
                        if ('' !== $url) {
4726
                            $urlparts = parse_url($url);
4727
                            if (!isset($urlparts['host'])) {
4728
                                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . mb_substr($wsdlparts['path'], 0, mb_strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4729
                            }
4730
                            if (!in_array($url, $imported_urls, true)) {
4731
                                $this->parseWSDL($url);
4732
                                ++$imported;
4733
                                $imported_urls[] = $url;
4734
                            }
4735
                        } else {
4736
                            $this->debug('Unexpected scenario: empty URL for unloaded import');
4737
                        }
4738
                    }
4739
                }
4740
            }
4741
        }
4742
        // add new data to operation data
4743
        foreach ($this->bindings as $binding => $bindingData) {
4744
            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4745
                foreach ($bindingData['operations'] as $operation => $data) {
4746
                    $this->debug('post-parse data gathering for ' . $operation);
4747
                    $this->bindings[$binding]['operations'][$operation]['input']  = isset($this->bindings[$binding]['operations'][$operation]['input']) ? array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) : $this->portTypes[$bindingData['portType']][$operation]['input'];
4748
                    $this->bindings[$binding]['operations'][$operation]['output'] = isset($this->bindings[$binding]['operations'][$operation]['output']) ? array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) : $this->portTypes[$bindingData['portType']][$operation]['output'];
4749
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
4750
                        $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
4751
                    }
4752
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
4753
                        $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
4754
                    }
4755
                    // Set operation style if necessary, but do not override one already provided
4756
                    if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4757
                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4758
                    }
4759
                    $this->bindings[$binding]['operations'][$operation]['transport']     = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4760
                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
4761
                    $this->bindings[$binding]['operations'][$operation]['endpoint']      = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4762
                }
4763
            }
4764
        }
4765
    }
4766
4767
    /**
4768
     * Parses the Wsdl document
4769
     *
4770
     * @param string $wsdl path or URL
4771
     * @return bool
4772
     */
4773
    private function parseWSDL($wsdl = '')
4774
    {
4775
        $this->debug("parse WSDL at path=$wsdl");
4776
        if ('' === $wsdl) {
4777
            $this->debug('no Wsdl passed to parseWSDL()!!');
4778
            $this->setError('no Wsdl passed to parseWSDL()!!');
4779
4780
            return false;
4781
        }
4782
4783
        // parse $wsdl for url format
4784
        $wsdl_props = parse_url($wsdl);
4785
        if (isset($wsdl_props['scheme']) && ('http' === $wsdl_props['scheme'] || 'https' === $wsdl_props['scheme'])) {
4786
            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4787
            // get Wsdl
4788
            $tr                 = new Soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4789
            $tr->request_method = 'GET';
4790
            $tr->useSOAPAction  = false;
4791
            if ($this->proxyhost && $this->proxyport) {
4792
                $tr->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
4793
            }
4794
            if ('' !== $this->authtype) {
4795
                $tr->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
4796
            }
4797
            $tr->setEncoding('gzip, deflate');
4798
            $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4799
            //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4800
            //$this->debug("WSDL response\n" . $tr->incoming_payload);
4801
            $this->appendDebug($tr->getDebug());
4802
            // catch errors
4803
            if (false !== ($err = $tr->getError())) {
4804
                $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: ' . $err;
4805
                $this->debug($errstr);
4806
                $this->setError($errstr);
4807
                unset($tr);
4808
4809
                return false;
4810
            }
4811
            unset($tr);
4812
            $this->debug('got WSDL URL');
4813
        } else {
4814
            // $wsdl is not http(s), so treat it as a file URL or plain file path
4815
            $path = $wsdl;
4816
            if (isset($wsdl_props['scheme']) && ('file' === $wsdl_props['scheme']) && isset($wsdl_props['path'])) {
4817
                $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4818
            }
4819
            $this->debug('getting WSDL file ' . $path);
4820
            if (false !== ($fp = @fopen($path, 'rb'))) {
4821
                $wsdl_string = '';
4822
                while (false !== ($data = fread($fp, 32768))) {
4823
                    $wsdl_string .= $data;
4824
                }
4825
                fclose($fp);
4826
            } else {
4827
                $errstr = "Bad path to WSDL file $path";
4828
                $this->debug($errstr);
4829
                $this->setError($errstr);
4830
4831
                return false;
4832
            }
4833
        }
4834
        $this->debug('Parse WSDL');
4835
        // end new code added
4836
        // Create an XML parser.
4837
        $this->parser = xml_parser_create();
4838
        // Set the options for parsing the XML data.
4839
        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4840
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4841
        // Set the object for the parser.
4842
        xml_set_object($this->parser, $this);
4843
        // Set the element handlers for the parser.
4844
        xml_set_element_handler($this->parser, 'start_element', 'end_element');
4845
        xml_set_character_data_handler($this->parser, 'character_data');
4846
        // Parse the XML file.
4847
        if (!xml_parse($this->parser, $wsdl_string, true)) {
4848
            // Display an error message.
4849
            $errstr = sprintf('XML error parsing WSDL from %s on line %d: %s', $wsdl, xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)));
4850
            $this->debug($errstr);
4851
            $this->debug("XML payload:\n" . $wsdl_string);
4852
            $this->setError($errstr);
4853
            xml_parser_free($this->parser);
4854
            unset($this->parser);
4855
4856
            return false;
4857
        }
4858
        // free the parser
4859
        xml_parser_free($this->parser);
4860
        unset($this->parser);
4861
        $this->debug('Parsing WSDL done');
4862
        // catch Wsdl parse errors
4863
        if ($this->getError()) {
4864
            return false;
4865
        }
4866
4867
        return true;
4868
    }
4869
4870
    /**
4871
     * Start-element handler
4872
     *
4873
     * @param string       $parser XML parser object
4874
     * @param string       $name   element name
4875
     * @param string|array $attrs  associative array of attributes
4876
     */
4877
    private function start_element($parser, $name, $attrs)
0 ignored issues
show
Unused Code introduced by
The method start_element() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
4878
    {
4879
        if ('schema' === $this->status) {
4880
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4881
            $this->appendDebug($this->currentSchema->getDebug());
4882
            $this->currentSchema->clearDebug();
4883
        } elseif (preg_match('/schema$/', $name)) {
4884
            $this->debug('Parsing WSDL schema');
4885
            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4886
            $this->status        = 'schema';
4887
            $this->currentSchema = new Nusoap_xmlschema('', '', $this->namespaces);
4888
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4889
            $this->appendDebug($this->currentSchema->getDebug());
4890
            $this->currentSchema->clearDebug();
4891
        } else {
4892
            // position in the total number of elements, starting from 0
4893
            $pos   = $this->position++;
4894
            $depth = $this->depth++;
4895
            // set self as current value for this depth
4896
            $this->depth_array[$depth] = $pos;
4897
            $this->message[$pos]       = ['cdata' => ''];
4898
            // process attributes
4899
            if (is_array($attrs) && count($attrs) > 0) {
4900
                // register namespace declarations
4901
                foreach ($attrs as $k => $v) {
4902
                    if (preg_match('/^xmlns/', $k)) {
4903
                        if (false !== ($ns_prefix = mb_substr(mb_strrchr($k, ':'), 1))) {
4904
                            $this->namespaces[$ns_prefix] = $v;
4905
                        } else {
4906
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4907
                        }
4908
                        if ('http://www.w3.org/2001/XMLSchema' === $v || 'http://www.w3.org/1999/XMLSchema' === $v || 'http://www.w3.org/2000/10/XMLSchema' === $v) {
4909
                            $this->XMLSchemaVersion  = $v;
4910
                            $this->namespaces['xsi'] = $v . '-instance';
4911
                        }
4912
                    }
4913
                }
4914
                // expand each attribute prefix to its namespace
4915
                foreach ($attrs as $k => $v) {
4916
                    $k = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
4917
                    if ('location' !== $k && 'soapAction' !== $k && 'namespace' !== $k) {
4918
                        $v = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
4919
                    }
4920
                    $eAttrs[$k] = $v;
4921
                }
4922
                $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 4915. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
4923
            } else {
4924
                $attrs = [];
4925
            }
4926
            // Set default prefix and namespace
4927
            // to prevent error Undefined variable $prefix and $namespace if (preg_match('/:/', $name)) return 0 or FALSE
4928
            $prefix    = '';
4929
            $namespace = '';
4930
            // get element prefix, namespace and name
4931
            if (preg_match('/:/', $name)) {
4932
                // get ns prefix
4933
                $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
4934
                // get ns
4935
                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4936
                // get unqualified name
4937
                $name = mb_substr(mb_strstr($name, ':'), 1);
4938
            }
4939
            // process attributes, expanding any prefixes to namespaces
4940
            // find status, register data
4941
            switch ($this->status) {
4942
                case 'message':
4943
                    if ('part' === $name) {
4944
                        if (isset($attrs['type'])) {
4945
                            $this->debug('msg ' . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
4946
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4947
                        }
4948
                        if (isset($attrs['element'])) {
4949
                            $this->debug('msg ' . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
4950
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
4951
                        }
4952
                    }
4953
4954
                    break;
4955
                case 'portType':
4956
                    switch ($name) {
4957
                        case 'operation':
4958
                            $this->currentPortOperation = $attrs['name'];
0 ignored issues
show
Bug Best Practice introduced by
The property currentPortOperation does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
4959
                            $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4960
                            if (isset($attrs['parameterOrder'])) {
4961
                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4962
                            }
4963
4964
                            break;
4965
                        case 'documentation':
4966
                            $this->documentation = true;
4967
4968
                            break;
4969
                        // merge input/output data
4970
                        default:
4971
                            $m                                                                                      = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4972
                            $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4973
4974
                            break;
4975
                    }
4976
4977
                    break;
4978
                case 'binding':
4979
                    switch ($name) {
4980
                        case 'binding':
4981
                            // get ns prefix
4982
4983
                            if (isset($attrs['style'])) {
4984
                                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4985
                            }
4986
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4987
4988
                            break;
4989
                        case 'header':
4990
                            $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4991
4992
                            break;
4993
                        case 'operation':
4994
                            if (isset($attrs['soapAction'])) {
4995
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4996
                            }
4997
                            if (isset($attrs['style'])) {
4998
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4999
                            }
5000
                            if (isset($attrs['name'])) {
5001
                                $this->currentOperation = $attrs['name'];
5002
                                $this->debug("current binding operation: $this->currentOperation");
5003
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name']     = $attrs['name'];
5004
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding']  = $this->currentBinding;
5005
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
5006
                            }
5007
5008
                            break;
5009
                        case 'input':
5010
                            $this->opStatus = 'input';
0 ignored issues
show
Bug Best Practice introduced by
The property opStatus does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
5011
5012
                            break;
5013
                        case 'output':
5014
                            $this->opStatus = 'output';
5015
5016
                            break;
5017
                        case 'body':
5018
                            if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5019
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5020
                            } else {
5021
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5022
                            }
5023
5024
                            break;
5025
                    }
5026
5027
                    break;
5028
                case 'service':
5029
                    switch ($name) {
5030
                        case 'port':
5031
                            $this->currentPort = $attrs['name'];
5032
                            $this->debug('current port: ' . $this->currentPort);
5033
                            $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5034
5035
                            break;
5036
                        case 'address':
5037
                            $this->ports[$this->currentPort]['location']                                = $attrs['location'];
5038
                            $this->ports[$this->currentPort]['bindingType']                             = $namespace;
5039
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
5040
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint']    = $attrs['location'];
5041
5042
                            break;
5043
                    }
5044
5045
                    break;
5046
            }
5047
            // set status
5048
            switch ($name) {
5049
                case 'import':
5050
                    if (isset($attrs['location'])) {
5051
                        $this->import[$attrs['namespace']][] = ['location' => $attrs['location'], 'loaded' => false];
5052
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
5053
                    } else {
5054
                        $this->import[$attrs['namespace']][] = ['location' => '', 'loaded' => true];
5055
                        if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
5056
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
5057
                        }
5058
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
5059
                    }
5060
5061
                    break;
5062
                //wait for schema
5063
                //case 'types':
5064
                //	$this->status = 'schema';
5065
                //	break;
5066
                case 'message':
5067
                    $this->status                   = 'message';
5068
                    $this->messages[$attrs['name']] = [];
5069
                    $this->currentMessage           = $attrs['name'];
5070
5071
                    break;
5072
                case 'portType':
5073
                    $this->status                    = 'portType';
5074
                    $this->portTypes[$attrs['name']] = [];
5075
                    $this->currentPortType           = $attrs['name'];
5076
5077
                    break;
5078
                case 'binding':
5079
                    if (isset($attrs['name'])) {
5080
                        // get binding name
5081
                        if (mb_strpos($attrs['name'], ':')) {
5082
                            $this->currentBinding = $this->getLocalPart($attrs['name']);
5083
                        } else {
5084
                            $this->currentBinding = $attrs['name'];
5085
                        }
5086
                        $this->status                                      = 'binding';
5087
                        $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5088
                        $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5089
                    }
5090
5091
                    break;
5092
                case 'service':
5093
                    $this->serviceName = $attrs['name'];
0 ignored issues
show
Bug Best Practice introduced by
The property serviceName does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
5094
                    $this->status      = 'service';
5095
                    $this->debug('current service: ' . $this->serviceName);
5096
5097
                    break;
5098
                case 'definitions':
5099
                    foreach ($attrs as $name => $value) {
0 ignored issues
show
introduced by
$name is overwriting one of the parameters of this function.
Loading history...
5100
                        $this->wsdl_info[$name] = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property wsdl_info does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
5101
                    }
5102
5103
                    break;
5104
            }
5105
        }
5106
    }
5107
5108
    /**
5109
     * End-element handler
5110
     *
5111
     * @param string $parser XML parser object
5112
     * @param string $name   element name
5113
     */
5114
    private function end_element($parser, $name)
0 ignored issues
show
Unused Code introduced by
The method end_element() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
5115
    {
5116
        // unset schema status
5117
        if (/*preg_match('/types$/', $name) ||*/
5118
        preg_match('/schema$/', $name)) {
5119
            $this->status = '';
5120
            $this->appendDebug($this->currentSchema->getDebug());
5121
            $this->currentSchema->clearDebug();
5122
            $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5123
            $this->debug('Parsing WSDL schema done');
5124
        }
5125
        if ('schema' === $this->status) {
5126
            $this->currentSchema->schemaEndElement($parser, $name);
5127
        } else {
5128
            // bring depth down a notch
5129
            $this->depth--;
5130
        }
5131
        // end documentation
5132
        if ($this->documentation) {
5133
            //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5134
            //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5135
            $this->documentation = false;
5136
        }
5137
    }
5138
5139
    /**
5140
     * Element content handler
5141
     *
5142
     * @param string $parser XML parser object
5143
     * @param string $data   element content
5144
     */
5145
    private function character_data($parser, $data)
0 ignored issues
show
Unused Code introduced by
The method character_data() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

5145
    private function character_data(/** @scrutinizer ignore-unused */ $parser, $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
5146
    {
5147
        $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5148
        if (isset($this->message[$pos]['cdata'])) {
5149
            $this->message[$pos]['cdata'] .= $data;
5150
        }
5151
        if ($this->documentation) {
5152
            $this->documentation .= $data;
5153
        }
5154
    }
5155
5156
    /**
5157
     * If authenticating, set user credentials here
5158
     *
5159
     * @param    string $username
5160
     * @param    string $password
5161
     * @param    string $authtype    (basic|digest|certificate|ntlm)
5162
     * @param    array  $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5163
     */
5164
    public function setCredentials($username, $password, $authtype = 'basic', $certRequest = [])
5165
    {
5166
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5167
        $this->appendDebug($this->varDump($certRequest));
5168
        $this->username    = $username;
5169
        $this->password    = $password;
5170
        $this->authtype    = $authtype;
5171
        $this->certRequest = $certRequest;
5172
    }
5173
5174
    /**
5175
     * @param $binding
5176
     * @return mixed
5177
     */
5178
    public function getBindingData($binding)
5179
    {
5180
        if (is_array($this->bindings[$binding])) {
5181
            return $this->bindings[$binding];
5182
        }
5183
    }
5184
5185
    /**
5186
     * Returns an assoc array of operation names => operation data
5187
     *
5188
     * @param string $portName    WSDL port name
5189
     * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5190
     * @return array
5191
     */
5192
    public function getOperations($portName = '', $bindingType = 'soap')
5193
    {
5194
        $ops = [];
5195
        if ('soap' === $bindingType) {
5196
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5197
        } elseif ('soap12' === $bindingType) {
5198
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5199
        } else {
5200
            $this->debug("getOperations bindingType $bindingType may not be supported");
5201
        }
5202
        $this->debug("getOperations for port '$portName' bindingType $bindingType");
5203
        // loop thru ports
5204
        foreach ($this->ports as $port => $portData) {
5205
            $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5206
            if ('' === $portName || $port === $portName) {
5207
                // binding type of port matches parameter
5208
                if ($portData['bindingType'] === $bindingType) {
5209
                    $this->debug("getOperations found port $port bindingType $bindingType");
5210
                    //$this->debug("port data: " . $this->varDump($portData));
5211
                    //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5212
                    // merge bindings
5213
                    if (isset($this->bindings[$portData['binding']]['operations'])) {
5214
                        $ops = array_merge($ops, $this->bindings[$portData['binding']]['operations']);
5215
                    }
5216
                }
5217
            }
5218
        }
5219
        if (0 === count($ops)) {
5220
            $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5221
        }
5222
5223
        return $ops;
5224
    }
5225
5226
    /**
5227
     * Returns an associative array of data necessary for calling an operation
5228
     *
5229
     * @param string $operation   name of operation
5230
     * @param string $bindingType type of binding eg: soap, soap12
5231
     * @return array
5232
     */
5233
    public function getOperationData($operation, $bindingType = 'soap')
5234
    {
5235
        if ('soap' === $bindingType) {
5236
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5237
        } elseif ('soap12' === $bindingType) {
5238
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5239
        }
5240
        // loop thru ports
5241
        foreach ($this->ports as $port => $portData) {
5242
            // binding type of port matches parameter
5243
            if ($portData['bindingType'] === $bindingType) {
5244
                // get binding
5245
                //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5246
                foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {
5247
                    // note that we could/should also check the namespace here
5248
                    if ($operation === $bOperation) {
5249
                        $opData = $this->bindings[$portData['binding']]['operations'][$operation];
5250
5251
                        return $opData;
5252
                    }
5253
                }
5254
            }
5255
        }
5256
    }
5257
5258
    /**
5259
     * Returns an associative array of data necessary for calling an operation
5260
     *
5261
     * @param string $soapAction  soapAction for operation
5262
     * @param string $bindingType type of binding eg: soap, soap12
5263
     * @return array
5264
     */
5265
    public function getOperationDataForSoapAction($soapAction, $bindingType = 'soap')
5266
    {
5267
        if ('soap' === $bindingType) {
5268
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5269
        } elseif ('soap12' === $bindingType) {
5270
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5271
        }
5272
        // loop thru ports
5273
        foreach ($this->ports as $port => $portData) {
5274
            // binding type of port matches parameter
5275
            if ($portData['bindingType'] === $bindingType) {
5276
                // loop through operations for the binding
5277
                foreach ($this->bindings[$portData['binding']]['operations'] as $bOperation => $opData) {
5278
                    if ($opData['soapAction'] === $soapAction) {
5279
                        return $opData;
5280
                    }
5281
                }
5282
            }
5283
        }
5284
    }
5285
5286
    /**
5287
     * Returns an array of information about a given type
5288
     * Returns false if no type exists by the given name
5289
     *
5290
     *     typeDef = array(
5291
     *     'elements' => array(), // refs to elements array
5292
     *    'restrictionBase' => '',
5293
     *    'phpType' => '',
5294
     *    'order' => '(sequence|all)',
5295
     *    'attrs' => array() // refs to attributes array
5296
     *    )
5297
     *
5298
     * @param string $type the type
5299
     * @param string $ns   namespace (not prefix) of the type
5300
     * @return mixed
5301
     * @see    Nusoap_xmlschema
5302
     */
5303
    public function getTypeDef($type, $ns)
5304
    {
5305
        $this->debug("in getTypeDef: type=$type, ns=$ns");
5306
        if ((!$ns) && isset($this->namespaces['tns'])) {
5307
            $ns = $this->namespaces['tns'];
5308
            $this->debug("in getTypeDef: type namespace forced to $ns");
5309
        }
5310
        if (!isset($this->schemas[$ns])) {
5311
            foreach ($this->schemas as $ns0 => $schema0) {
5312
                if (0 === strcasecmp($ns, $ns0)) {
5313
                    $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5314
                    $ns = $ns0;
5315
                    break;
5316
                }
5317
            }
5318
        }
5319
        if (isset($this->schemas[$ns])) {
5320
            $this->debug("in getTypeDef: have schema for namespace $ns");
5321
            for ($i = 0, $iMax = count($this->schemas[$ns]); $i < $iMax; ++$i) {
5322
                $xs = $this->schemas[$ns][$i];
5323
                $t  = $xs->getTypeDef($type);
5324
                $this->appendDebug($xs->getDebug());
5325
                $xs->clearDebug();
5326
                if ($t) {
5327
                    $this->debug("in getTypeDef: found type $type");
5328
                    if (!isset($t['phpType'])) {
5329
                        // get info for type to tack onto the element
5330
                        $uqType = mb_substr($t['type'], mb_strrpos($t['type'], ':') + 1);
5331
                        $ns     = mb_substr($t['type'], 0, mb_strrpos($t['type'], ':'));
5332
                        $etype  = $this->getTypeDef($uqType, $ns);
5333
                        if ($etype) {
5334
                            $this->debug("found type for [element] $type:");
5335
                            $this->debug($this->varDump($etype));
5336
                            if (isset($etype['phpType'])) {
5337
                                $t['phpType'] = $etype['phpType'];
5338
                            }
5339
                            if (isset($etype['elements'])) {
5340
                                $t['elements'] = $etype['elements'];
5341
                            }
5342
                            if (isset($etype['attrs'])) {
5343
                                $t['attrs'] = $etype['attrs'];
5344
                            }
5345
                        } else {
5346
                            $this->debug("did not find type for [element] $type");
5347
                        }
5348
                    }
5349
5350
                    return $t;
5351
                }
5352
            }
5353
            $this->debug("in getTypeDef: did not find type $type");
5354
        } else {
5355
            $this->debug("in getTypeDef: do not have schema for namespace $ns");
5356
        }
5357
5358
        return false;
5359
    }
5360
5361
    /**
5362
     * Prints html description of services
5363
     */
5364
    public function webDescription()
5365
    {
5366
        global $_SERVER;
5367
        if (null !== $_SERVER) {
5368
            $PHP_SELF = $_SERVER['PHP_SELF'];
5369
        } else {
5370
            $this->setError(' _SERVER is not available');
5371
        }
5372
5373
        $b = '
5374
		<html><head><title>NuSOAP: ' . $this->serviceName . '</title>
5375
		<style type="text/css">
5376
		    body    { font-family: arial sans-serif; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5377
		    p       { font-family: arial sans-serif; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5378
		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5379
		    ul      { margin-top: 10px; margin-left: 20px; }
5380
		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
5381
		    .content{
5382
			margin-left: 0px; padding-bottom: 2em; }
5383
		    .nav {
5384
			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5385
			margin-top: 10px; margin-left: 0px; color: #000000;
5386
			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5387
		    .title {
5388
			font-family: arial sans-serif; font-size: 26px; color: #ffffff;
5389
			background-color: #999999; width: 100%;
5390
			margin-left: 0px; margin-right: 0px;
5391
			padding-top: 10px; padding-bottom: 10px;}
5392
		    .hidden {
5393
			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5394
			font-family: arial sans-serif; overflow: hidden; width: 600;
5395
			padding: 20px; font-size: 10px; background-color: #999999;
5396
			layer-background-color:#FFFFFF; }
5397
		    a,a:active  { color: charcoal; font-weight: bold; }
5398
		    a:visited   { color: #666666; font-weight: bold; }
5399
		    a:hover     { color: cc3300; font-weight: bold; }
5400
		</style>
5401
		<script language="JavaScript" type="text/javascript">
5402
		<!--
5403
		// POP-UP CAPTIONS...
5404
		function lib_bwcheck(){ //Browsercheck (needed)
5405
		    this.ver=navigator.appVersion
5406
		    this.agent=navigator.userAgent
5407
		    this.dom=document.getElementById?1:0
5408
		    this.opera5=this.agent.indexOf("Opera 5")>-1
5409
		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5410
		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5411
		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5412
		    this.ie=this.ie4||this.ie5||this.ie6
5413
		    this.mac=this.agent.indexOf("Mac")>-1
5414
		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5415
		    this.ns4=(document.layers && !this.dom)?1:0;
5416
		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5417
		    return this
5418
		}
5419
		var bw = new lib_bwcheck()
5420
		//Makes crossbrowser object.
5421
		function makeObj(obj){
5422
		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5423
		    if(!this.evnt) return false
5424
		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5425
		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5426
		    this.writeIt=b_writeIt;
5427
		    return this
5428
		}
5429
		// A unit of measure that will be added when setting the position of a layer.
5430
		//var px = bw.ns4||window.opera?"":"px";
5431
		function b_writeIt(text){
5432
		    if (bw.ns4){this.wref.write(text);this.wref.close()}
5433
		    else this.wref.innerHTML = text
5434
		}
5435
		//Shows the messages
5436
		var oDesc;
5437
		function popup(divid){
5438
		    if(oDesc = new makeObj(divid)){
5439
			oDesc.css.visibility = "visible"
5440
		    }
5441
		}
5442
		function popout(){ // Hides message
5443
		    if(oDesc) oDesc.css.visibility = "hidden"
5444
		}
5445
		//-->
5446
		</script>
5447
		</head>
5448
		<body>
5449
		<div class=content>
5450
			<br><br>
5451
			<div class=title>' . $this->serviceName . '</div>
5452
			<div class=nav>
5453
				<p>View the <a href="' . $PHP_SELF . '?wsdl">WSDL</a> for the service.
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $PHP_SELF does not seem to be defined for all execution paths leading up to this point.
Loading history...
5454
				Click on an operation name to view it&apos;s details.</p>
5455
				<ul>';
5456
        foreach ($this->getOperations() as $op => $data) {
5457
            $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5458
            // create hidden div
5459
            $b .= "<div id='$op' class='hidden'>
5460
                    <a href='#' onclick='popout()'><span style='color: #ffffff; '>Close</span></a><br><br>";
5461
            foreach ($data as $donnie => $marie) {
5462
                // loop through opdata
5463
                if ('input' === $donnie || 'output' === $donnie) {
5464
                    // show input/output data
5465
                    $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
5466
                    foreach ($marie as $captain => $tenille) {
5467
                        // loop through data
5468
                        if ('parts' === $captain) {
5469
                            // loop thru parts
5470
                            $b .= "&nbsp;&nbsp;$captain:<br>";
5471
                            //if(is_array($tenille)){
5472
                            foreach ($tenille as $joanie => $chachi) {
5473
                                $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
5474
                            }
5475
                            //}
5476
                        } else {
5477
                            $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
5478
                        }
5479
                    }
5480
                } else {
5481
                    $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
5482
                }
5483
            }
5484
            $b .= '</div>';
5485
        }
5486
        $b .= '
5487
				<ul>
5488
			</div>
5489
		</div></body></html>';
5490
5491
        return $b;
5492
    }
5493
5494
    /**
5495
     * Serialize the parsed Wsdl
5496
     *
5497
     * @param mixed $debug whether to put debug=1 in endpoint URL
5498
     * @return string serialization of WSDL
5499
     */
5500
    public function serialize($debug = 0)
5501
    {
5502
        $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5503
        $xml .= "\n<definitions";
5504
        foreach ($this->namespaces as $k => $v) {
5505
            $xml .= " xmlns:$k=\"$v\"";
5506
        }
5507
        // 10.9.02 - add poulter fix for Wsdl and tns declarations
5508
        if (isset($this->namespaces['Wsdl'])) {
5509
            $xml .= ' xmlns="' . $this->namespaces['Wsdl'] . '"';
5510
        }
5511
        if (isset($this->namespaces['tns'])) {
5512
            $xml .= ' targetNamespace="' . $this->namespaces['tns'] . '"';
5513
        }
5514
        $xml .= '>';
5515
        // imports
5516
        if (is_array($this->import) && count($this->import) > 0) {
5517
            foreach ($this->import as $ns => $list) {
5518
                foreach ($list as $ii) {
5519
                    if ('' !== $ii['location']) {
5520
                        $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" >';
5521
                    } else {
5522
                        $xml .= '<import namespace="' . $ns . '" >';
5523
                    }
5524
                }
5525
            }
5526
        }
5527
        // types
5528
        if (is_array($this->schemas) && count($this->schemas) >= 1) {
5529
            $xml .= "\n<types>\n";
5530
            foreach ($this->schemas as $ns => $list) {
5531
                foreach ($list as $xs) {
5532
                    $xml .= $xs->serializeSchema();
5533
                }
5534
            }
5535
            $xml .= '</types>';
5536
        }
5537
        // messages
5538
        if (is_array($this->messages) && count($this->messages) >= 1) {
5539
            foreach ($this->messages as $msgName => $msgParts) {
5540
                $xml .= "\n<message name=\"" . $msgName . '">';
5541
                if (is_array($msgParts)) {
5542
                    foreach ($msgParts as $partName => $partType) {
5543
                        // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5544
                        if (mb_strpos($partType, ':')) {
5545
                            $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
0 ignored issues
show
Bug introduced by
It seems like $this->getPrefix($partType) can also be of type false; however, parameter $ns of Nusoap_base::getPrefixFromNamespace() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

5545
                            $typePrefix = $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($partType));
Loading history...
5546
                        } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5547
                            // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5548
                            $typePrefix = 'xsd';
5549
                        } else {
5550
                            foreach ($this->typemap as $ns => $types) {
5551
                                if (isset($types[$partType])) {
5552
                                    $typePrefix = $this->getPrefixFromNamespace($ns);
5553
                                }
5554
                            }
5555
                            if (!isset($typePrefix)) {
5556
                                die("$partType has no namespace!");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
5557
                            }
5558
                        }
5559
                        $ns        = $this->getNamespaceFromPrefix($typePrefix);
5560
                        $localPart = $this->getLocalPart($partType);
5561
                        $typeDef   = $this->getTypeDef($localPart, $ns);
0 ignored issues
show
Bug introduced by
It seems like $ns can also be of type false; however, parameter $ns of Wsdl::getTypeDef() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

5561
                        $typeDef   = $this->getTypeDef($localPart, /** @scrutinizer ignore-type */ $ns);
Loading history...
5562
                        if ('element' === $typeDef['typeClass']) {
5563
                            $elementortype = 'element';
5564
                            if ('^' === mb_substr($localPart, -1)) {
5565
                                $localPart = mb_substr($localPart, 0, -1);
5566
                            }
5567
                        } else {
5568
                            $elementortype = 'type';
5569
                        }
5570
                        $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" >';
5571
                    }
5572
                }
5573
                $xml .= '</message>';
5574
            }
5575
        }
5576
        // bindings & porttypes
5577
        if (is_array($this->bindings) && count($this->bindings) >= 1) {
5578
            $binding_xml  = '';
5579
            $portType_xml = '';
5580
            foreach ($this->bindings as $bindingName => $attrs) {
5581
                $binding_xml  .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5582
                $binding_xml  .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '">';
5583
                $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5584
                foreach ($attrs['operations'] as $opName => $opParts) {
5585
                    $binding_xml .= "\n" . '  <operation name="' . $opName . '">';
5586
                    $binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $opParts['style'] . '">';
5587
                    $enc_style   = '';
5588
                    if (isset($opParts['input']['encodingStyle']) && '' !== $opParts['input']['encodingStyle']) {
5589
                        $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5590
                    }
5591
                    $binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '></input>';
5592
                    $enc_style   = '';
5593
                    if (isset($opParts['output']['encodingStyle']) && '' !== $opParts['output']['encodingStyle']) {
5594
                        $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5595
                    }
5596
                    $binding_xml  .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '></output>';
5597
                    $binding_xml  .= "\n" . '  </operation>';
5598
                    $portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
5599
                    if (isset($opParts['parameterOrder'])) {
5600
                        $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5601
                    }
5602
                    $portType_xml .= '>';
5603
                    if (isset($opParts['documentation']) && '' !== $opParts['documentation']) {
5604
                        $portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5605
                    }
5606
                    $portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '">';
5607
                    $portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '">';
5608
                    $portType_xml .= "\n" . '  </operation>';
5609
                }
5610
                $portType_xml .= "\n" . '</portType>';
5611
                $binding_xml  .= "\n" . '</binding>';
5612
            }
5613
            $xml .= $portType_xml . $binding_xml;
5614
        }
5615
        // services
5616
        $xml .= "\n<service name=\"" . $this->serviceName . '">';
5617
        if (is_array($this->ports) && count($this->ports) >= 1) {
5618
            foreach ($this->ports as $pName => $attrs) {
5619
                $xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5620
                $xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '">';
5621
                $xml .= "\n" . '  </port>';
5622
            }
5623
        }
5624
        $xml .= "\n" . '</service>';
5625
5626
        return $xml . "\n</definitions>";
5627
    }
5628
5629
    /**
5630
     * Determine whether a set of parameters are unwrapped
5631
     * when they are expect to be wrapped, Microsoft-style.
5632
     *
5633
     * @param string $type       the type (element name) of the wrapper
5634
     * @param array  $parameters the parameter values for the SOAP call
5635
     * @return bool whether they parameters are unwrapped (and should be wrapped)
5636
     */
5637
    private function parametersMatchWrapped($type, &$parameters)
5638
    {
5639
        $this->debug("in parametersMatchWrapped type=$type, parameters=");
5640
        $this->appendDebug($this->varDump($parameters));
5641
        // split type into namespace:unqualified-type
5642
        if (mb_strpos($type, ':')) {
5643
            $uqType = mb_substr($type, mb_strrpos($type, ':') + 1);
5644
            $ns     = mb_substr($type, 0, mb_strrpos($type, ':'));
5645
            $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5646
            if ($this->getNamespaceFromPrefix($ns)) {
5647
                $ns = $this->getNamespaceFromPrefix($ns);
5648
                $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5649
            }
5650
        } else {
5651
            // TODO: should the type be compared to types in XSD, and the namespace
5652
            // set to XSD if the type matches?
5653
            $this->debug("in parametersMatchWrapped: No namespace for type $type");
5654
            $ns     = '';
5655
            $uqType = $type;
5656
        }
5657
5658
        // get the type information
5659
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5660
            $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5661
5662
            return false;
5663
        }
5664
        $this->debug('in parametersMatchWrapped: found typeDef=');
5665
        $this->appendDebug($this->varDump($typeDef));
5666
        if ('^' === mb_substr($uqType, -1)) {
5667
            $uqType = mb_substr($uqType, 0, -1);
5668
        }
5669
        $phpType   = $typeDef['phpType'];
5670
        $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5671
        $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5672
        // we expect a complexType or element of complexType
5673
        if ('struct' !== $phpType) {
5674
            $this->debug('in parametersMatchWrapped: not a struct');
5675
5676
            return false;
5677
        }
5678
5679
        // see whether the parameter names match the elements
5680
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5681
            $elements = 0;
5682
            $matches  = 0;
5683
            foreach ($typeDef['elements'] as $name => $attrs) {
5684
                if (isset($parameters[$name])) {
5685
                    $this->debug("in parametersMatchWrapped: have parameter named $name");
5686
                    ++$matches;
5687
                } else {
5688
                    $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5689
                }
5690
                ++$elements;
5691
            }
5692
5693
            $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5694
            if (0 === $matches) {
5695
                return false;
5696
            }
5697
5698
            return true;
5699
        }
5700
5701
        // since there are no elements for the type, if the user passed no
5702
        // parameters, the parameters match wrapped.
5703
        $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5704
5705
        return 0 === count($parameters);
5706
    }
5707
5708
    /**
5709
     * Serialize PHP values according to a WSDL message definition
5710
     * contrary to the method name, this is not limited to RPC
5711
     *
5712
     * TODO
5713
     * - multi-ref serialization
5714
     * - validate PHP values against type definitions, return errors if invalid
5715
     *
5716
     * @param string $operation   operation name
5717
     * @param string $direction   (input|output)
5718
     * @param mixed  $parameters  parameter value(s)
5719
     * @param string $bindingType (soap|soap12)
5720
     * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5721
     */
5722
    public function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap')
5723
    {
5724
        $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5725
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5726
        if ('input' !== $direction && 'output' !== $direction) {
5727
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5728
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5729
5730
            return false;
5731
        }
5732
        if (!$opData = $this->getOperationData($operation, $bindingType)) {
5733
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5734
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5735
5736
            return false;
5737
        }
5738
        $this->debug('in serializeRPCParameters: opData:');
5739
        $this->appendDebug($this->varDump($opData));
5740
        // Get encoding style for output and set to current
5741
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5742
        if (('input' === $direction) && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] !== $encodingStyle)) {
5743
            $encodingStyle = $opData['output']['encodingStyle'];
5744
            $enc_style     = $encodingStyle;
0 ignored issues
show
Unused Code introduced by
The assignment to $enc_style is dead and can be removed.
Loading history...
5745
        }
5746
5747
        // set input params
5748
        $xml = '';
5749
        if (isset($opData[$direction]['parts']) && count($opData[$direction]['parts']) > 0) {
5750
            $parts      = &$opData[$direction]['parts'];
5751
            $part_count = count($parts);
5752
            $style      = $opData['style'];
5753
            $use        = $opData[$direction]['use'];
5754
            $this->debug("have $part_count part(s) to serialize using $style/$use");
5755
            if (is_array($parameters)) {
5756
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5757
                $parameter_count     = count($parameters);
5758
                $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5759
                // check for Microsoft-style wrapped parameters
5760
                if ('document' === $style && 'literal' === $use && 1 === $part_count && isset($parts['parameters'])) {
5761
                    $this->debug('check whether the caller has wrapped the parameters');
5762
                    if ('output' === $direction && 'arraySimple' === $parametersArrayType && 1 === $parameter_count) {
5763
                        // TODO: consider checking here for double-wrapping, when
5764
                        // service function wraps, then NuSOAP wraps again
5765
                        $this->debug("change simple array to associative with 'parameters' element");
5766
                        $parameters['parameters'] = $parameters[0];
5767
                        unset($parameters[0]);
5768
                    }
5769
                    if (('arrayStruct' === $parametersArrayType || 0 === $parameter_count) && !isset($parameters['parameters'])) {
5770
                        $this->debug('check whether caller\'s parameters match the wrapped ones');
5771
                        if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5772
                            $this->debug('wrap the parameters for the caller');
5773
                            $parameters      = ['parameters' => $parameters];
5774
                            $parameter_count = 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $parameter_count is dead and can be removed.
Loading history...
5775
                        }
5776
                    }
5777
                }
5778
                foreach ($parts as $name => $type) {
5779
                    $this->debug("serializing part $name of type $type");
5780
                    // Track encoding style
5781
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle !== $opData[$direction]['encodingStyle']) {
5782
                        $encodingStyle = $opData[$direction]['encodingStyle'];
5783
                        $enc_style     = $encodingStyle;
5784
                    } else {
5785
                        $enc_style = false;
5786
                    }
5787
                    // NOTE: add error handling here
5788
                    // if serializeType returns false, then catch global error and fault
5789
                    if ('arraySimple' === $parametersArrayType) {
5790
                        $p = array_shift($parameters);
5791
                        $this->debug('calling serializeType w/indexed param');
5792
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5793
                    } elseif (isset($parameters[$name])) {
5794
                        $this->debug('calling serializeType w/named param');
5795
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5796
                    } else {
5797
                        // TODO: only send nillable
5798
                        $this->debug('calling serializeType w/null param');
5799
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5800
                    }
5801
                }
5802
            } else {
5803
                $this->debug('no parameters passed.');
5804
            }
5805
        }
5806
        $this->debug("serializeRPCParameters returning: $xml");
5807
5808
        return $xml;
5809
    }
5810
5811
    /**
5812
     * Serialize a PHP value according to a WSDL message definition
5813
     *
5814
     * TODO
5815
     * - multi-ref serialization
5816
     * - validate PHP values against type definitions, return errors if invalid
5817
     *
5818
     * @param string $operation  operation name
5819
     * @param string $direction  (input|output)
5820
     * @param mixed  $parameters parameter value(s)
5821
     * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5822
     * @deprecated
5823
     */
5824
    public function serializeParameters($operation, $direction, $parameters)
5825
    {
5826
        $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5827
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5828
        if ('input' !== $direction && 'output' !== $direction) {
5829
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5830
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5831
5832
            return false;
5833
        }
5834
        if (!$opData = $this->getOperationData($operation)) {
5835
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5836
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5837
5838
            return false;
5839
        }
5840
        $this->debug('opData:');
5841
        $this->appendDebug($this->varDump($opData));
5842
        // Get encoding style for output and set to current
5843
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5844
        if (('input' === $direction) && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] !== $encodingStyle)) {
5845
            $encodingStyle = $opData['output']['encodingStyle'];
5846
            $enc_style     = $encodingStyle;
0 ignored issues
show
Unused Code introduced by
The assignment to $enc_style is dead and can be removed.
Loading history...
5847
        }
5848
5849
        // set input params
5850
        $xml = '';
5851
        if (isset($opData[$direction]['parts']) && count($opData[$direction]['parts']) > 0) {
5852
            $use = $opData[$direction]['use'];
5853
            $this->debug("use=$use");
5854
            $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5855
            if (is_array($parameters)) {
5856
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5857
                $this->debug('have ' . $parametersArrayType . ' parameters');
5858
                foreach ($opData[$direction]['parts'] as $name => $type) {
5859
                    $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
5860
                    // Track encoding style
5861
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle !== $opData[$direction]['encodingStyle']) {
5862
                        $encodingStyle = $opData[$direction]['encodingStyle'];
5863
                        $enc_style     = $encodingStyle;
5864
                    } else {
5865
                        $enc_style = false;
5866
                    }
5867
                    // NOTE: add error handling here
5868
                    // if serializeType returns false, then catch global error and fault
5869
                    if ('arraySimple' === $parametersArrayType) {
5870
                        $p = array_shift($parameters);
5871
                        $this->debug('calling serializeType w/indexed param');
5872
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5873
                    } elseif (isset($parameters[$name])) {
5874
                        $this->debug('calling serializeType w/named param');
5875
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5876
                    } else {
5877
                        // TODO: only send nillable
5878
                        $this->debug('calling serializeType w/null param');
5879
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5880
                    }
5881
                }
5882
            } else {
5883
                $this->debug('no parameters passed.');
5884
            }
5885
        }
5886
        $this->debug("serializeParameters returning: $xml");
5887
5888
        return $xml;
5889
    }
5890
5891
    /**
5892
     * Serializes a PHP value according a given type definition
5893
     *
5894
     * @param string      $name          name of value (part or element)
5895
     * @param string      $type          XML schema type of value (type or element)
5896
     * @param mixed       $value         a native PHP value (parameter value)
5897
     * @param string      $use           use for part (encoded|literal)
5898
     * @param bool|string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5899
     * @param bool        $unqualified   a kludge for what should be XML namespace form handling
5900
     * @return string value serialized as an XML string
5901
     */
5902
    private function serializeType($name, $type, $value, $use = 'encoded', $encodingStyle = false, $unqualified = false)
5903
    {
5904
        $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? 'unqualified' : 'qualified'));
5905
        $this->appendDebug('value=' . $this->varDump($value));
5906
        if ('encoded' === $use && $encodingStyle) {
5907
            $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
0 ignored issues
show
Bug introduced by
Are you sure $encodingStyle of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

5907
            $encodingStyle = ' SOAP-ENV:encodingStyle="' . /** @scrutinizer ignore-type */ $encodingStyle . '"';
Loading history...
5908
        }
5909
5910
        // if a Soapval has been supplied, let its type override the WSDL
5911
        if (is_object($value) && 'soapval' === get_class($value)) {
5912
            if ($value->type_ns) {
5913
                $type      = $value->type_ns . ':' . $value->type;
5914
                $forceType = true;
5915
                $this->debug("in serializeType: Soapval overrides type to $type");
5916
            } elseif ($value->type) {
5917
                $type      = $value->type;
5918
                $forceType = true;
5919
                $this->debug("in serializeType: Soapval overrides type to $type");
5920
            } else {
5921
                $forceType = false;
5922
                $this->debug('in serializeType: Soapval does not override type');
5923
            }
5924
            $attrs = $value->attributes;
5925
            $value = $value->value;
5926
            $this->debug("in serializeType: Soapval overrides value to $value");
5927
            if ($attrs) {
5928
                if (!is_array($value)) {
5929
                    $value['!'] = $value;
5930
                }
5931
                foreach ($attrs as $n => $v) {
5932
                    $value['!' . $n] = $v;
5933
                }
5934
                $this->debug('in serializeType: Soapval provides attributes');
5935
            }
5936
        } else {
5937
            $forceType = false;
5938
        }
5939
5940
        $xml = '';
5941
        if (mb_strpos($type, ':')) {
5942
            $uqType = mb_substr($type, mb_strrpos($type, ':') + 1);
5943
            $ns     = mb_substr($type, 0, mb_strrpos($type, ':'));
5944
            $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5945
            if ($this->getNamespaceFromPrefix($ns)) {
5946
                $ns = $this->getNamespaceFromPrefix($ns);
5947
                $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5948
            }
5949
5950
            if ($ns === $this->XMLSchemaVersion || 'http://schemas.xmlsoap.org/soap/encoding/' === $ns) {
5951
                $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5952
                $elementNS = '';
5953
                if ($unqualified && 'literal' === $use) {
5954
                    $elementNS = ' xmlns=""';
5955
                }
5956
                if (null === $value) {
5957
                    if ('literal' === $use) {
5958
                        // TODO: depends on minOccurs
5959
                        $xml = "<$name$elementNS>";
5960
                    } else {
5961
                        // TODO: depends on nillable, which should be checked before calling this method
5962
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
0 ignored issues
show
Bug introduced by
Are you sure $this->getPrefixFromNamespace($ns) of type mixed|false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

5962
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace($ns) . ":$uqType\">";
Loading history...
5963
                    }
5964
                    $this->debug("in serializeType: returning: $xml");
5965
5966
                    return $xml;
5967
                }
5968
                if ('Array' === $uqType) {
5969
                    // JBoss/Axis does this sometimes
5970
                    return $this->serialize_val($value, $name, false, false, false, false, $use);
5971
                }
5972
                if ('bool' === $uqType) {
5973
                    if ((is_string($value) && 'false' === $value) || (!$value)) {
5974
                        $value = 'false';
5975
                    } else {
5976
                        $value = 'true';
5977
                    }
5978
                }
5979
                if ('string' === $uqType && is_string($value)) {
5980
                    $value = $this->expandEntities($value);
5981
                }
5982
                if (('long' === $uqType || 'unsignedLong' === $uqType) && is_float($value)) {
5983
                    $value = sprintf('%.0lf', $value);
5984
                }
5985
                // it's a scalar
5986
                // TODO: what about null/nil values?
5987
                // check type isn't a custom type extending xmlschema namespace
5988
                if (!$this->getTypeDef($uqType, $ns)) {
5989
                    if ('literal' === $use) {
5990
                        $xml = "<$name$elementNS>$value</$name>";
5991
                        if ($forceType) {
5992
                            $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5993
                        }
5994
                    } else {
5995
                        $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5996
                    }
5997
                    $this->debug("in serializeType: returning: $xml");
5998
5999
                    return $xml;
6000
                }
6001
                $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
6002
            } elseif ('http://xml.apache.org/xml-soap' === $ns) {
6003
                $this->debug('in serializeType: appears to be Apache SOAP type');
6004
                if ('Map' === $uqType) {
6005
                    $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6006
                    if (!$tt_prefix) {
6007
                        $this->debug('in serializeType: Add namespace for Apache SOAP type');
6008
                        $tt_prefix                    = 'ns' . mt_rand(1000, 9999);
6009
                        $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
6010
                        // force this to be added to usedNamespaces
6011
                        $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6012
                    }
6013
                    $contents = '';
6014
                    foreach ($value as $k => $v) {
6015
                        $this->debug("serializing map element: key $k, value $v");
6016
                        $contents .= '<item>';
6017
                        $contents .= $this->serialize_val($k, 'key', false, false, false, false, $use);
6018
                        $contents .= $this->serialize_val($v, 'value', false, false, false, false, $use);
6019
                        $contents .= '</item>';
6020
                    }
6021
                    if ('literal' === $use) {
6022
                        $xml = "<$name>$contents</$name>";
6023
                        if ($forceType) {
6024
                            $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
6025
                        }
6026
                    } else {
6027
                        $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6028
                    }
6029
                    $this->debug("in serializeType: returning: $xml");
6030
6031
                    return $xml;
6032
                }
6033
                $this->debug('in serializeType: Apache SOAP type, but only support Map');
6034
            }
6035
        } else {
6036
            // TODO: should the type be compared to types in XSD, and the namespace
6037
            // set to XSD if the type matches?
6038
            $this->debug("in serializeType: No namespace for type $type");
6039
            $ns     = '';
6040
            $uqType = $type;
6041
        }
6042
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
6043
            $this->setError("$type ($uqType) is not a supported type.");
6044
            $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6045
6046
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
6047
        }
6048
6049
        $this->debug('in serializeType: found typeDef');
6050
        $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6051
        if ('^' === mb_substr($uqType, -1)) {
6052
            $uqType = mb_substr($uqType, 0, -1);
6053
        }
6054
        if (!isset($typeDef['phpType'])) {
6055
            $this->setError("$type ($uqType) has no phpType.");
6056
            $this->debug("in serializeType: $type ($uqType) has no phpType.");
6057
6058
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
6059
        }
6060
        $phpType = $typeDef['phpType'];
6061
        $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
6062
        // if php type == struct, map value to the <all> element names
6063
        if ('struct' === $phpType) {
6064
            if (isset($typeDef['typeClass']) && 'element' === $typeDef['typeClass']) {
6065
                $elementName = $uqType;
6066
                $elementNS   = ' xmlns=""';
6067
                if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6068
                    $elementNS = " xmlns=\"$ns\"";
6069
                }
6070
            } else {
6071
                $elementName = $name;
6072
                $elementNS   = '';
6073
                if ($unqualified) {
6074
                    $elementNS = ' xmlns=""';
6075
                }
6076
            }
6077
            if (null === $value) {
6078
                if ('literal' === $use) {
6079
                    // TODO: depends on minOccurs and nillable
6080
                    $xml = "<$elementName$elementNS>";
6081
                } else {
6082
                    $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6083
                }
6084
                $this->debug("in serializeType: returning: $xml");
6085
6086
                return $xml;
6087
            }
6088
            if (is_object($value)) {
6089
                $value = get_object_vars($value);
6090
            }
6091
            if (is_array($value)) {
6092
                $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6093
                if ('literal' === $use) {
6094
                    $xml = "<$elementName$elementNS$elementAttrs>";
6095
                    if ($forceType) {
6096
                        $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6097
                    }
6098
                } else {
6099
                    $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6100
                }
6101
6102
                if (isset($typeDef['simpleContent']) && 'true' === $typeDef['simpleContent']) {
6103
                    if (isset($value['!'])) {
6104
                        $xml .= $value['!'];
6105
                        $this->debug("in serializeType: serialized simpleContent for type $type");
6106
                    } else {
6107
                        $this->debug("in serializeType: no simpleContent to serialize for type $type");
6108
                    }
6109
                } else {
6110
                    // complexContent
6111
                    $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6112
                }
6113
                $xml .= "</$elementName>";
6114
            } else {
6115
                $this->debug('in serializeType: phpType is struct, but value is not an array');
6116
                $this->setError('phpType is struct, but value is not an array: see debug output for details');
6117
                $xml = '';
6118
            }
6119
        } elseif ('array' === $phpType) {
6120
            if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6121
                $elementNS = " xmlns=\"$ns\"";
6122
            } else {
6123
                $elementNS = '';
6124
                if ($unqualified) {
6125
                    $elementNS = ' xmlns=""';
6126
                }
6127
            }
6128
            if (null === $value) {
6129
                if ('literal' === $use) {
6130
                    // TODO: depends on minOccurs
6131
                    $xml = "<$name$elementNS>";
6132
                } else {
6133
                    $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\""
6134
                           . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
0 ignored issues
show
Bug introduced by
Are you sure $this->getPrefixFromName...ap.org/soap/encoding/') of type mixed|false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6134
                           . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
Loading history...
6135
                           . ':Array" '
6136
                           . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6137
                           . ':arrayType="'
6138
                           . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
0 ignored issues
show
Bug introduced by
Are you sure $this->getPrefixFromName...$typeDef['arrayType'])) of type mixed|false can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6138
                           . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
Loading history...
Bug introduced by
It seems like $this->getPrefix($typeDef['arrayType']) can also be of type false; however, parameter $ns of Nusoap_base::getPrefixFromNamespace() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6138
                           . $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($typeDef['arrayType']))
Loading history...
6139
                           . ':'
6140
                           . $this->getLocalPart($typeDef['arrayType'])
6141
                           . '[0]"/>';
6142
                }
6143
                $this->debug("in serializeType: returning: $xml");
6144
6145
                return $xml;
6146
            }
6147
            if (isset($typeDef['multidimensional'])) {
6148
                $nv = [];
6149
                foreach ($value as $v) {
6150
                    $cols = ',' . count($v);
6151
                    $nv   = array_merge($nv, $v);
6152
                }
6153
                $value = $nv;
6154
            } else {
6155
                $cols = '';
6156
            }
6157
            if (is_array($value) && count($value) >= 1) {
6158
                $rows     = count($value);
6159
                $contents = '';
6160
                foreach ($value as $k => $v) {
6161
                    $this->debug("serializing array element: $k, " . (is_array($v) ? 'array' : $v) . " of type: $typeDef[arrayType]");
6162
                    //if (strpos($typeDef['arrayType'], ':') ) {
6163
                    if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'], true)) {
6164
                        $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6165
                    } else {
6166
                        $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6167
                    }
6168
                }
6169
            } else {
6170
                $rows     = 0;
6171
                $contents = null;
6172
            }
6173
            // TODO: for now, an empty value will be serialized as a zero element
6174
            // array.  Revisit this when coding the handling of null/nil values.
6175
            if ('literal' === $use) {
6176
                $xml = "<$name$elementNS>" . $contents . "</$name>";
6177
            } else {
6178
                $xml = "<$name$elementNS xsi:type=\""
6179
                       . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6180
                       . ':Array" '
6181
                       . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6182
                       . ':arrayType="'
6183
                       . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6184
                       . ':'
6185
                       . $this->getLocalPart($typeDef['arrayType'])
6186
                       . "[$rows$cols]\">"
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cols does not seem to be defined for all execution paths leading up to this point.
Loading history...
6187
                       . $contents
6188
                       . "</$name>";
6189
            }
6190
        } elseif ('scalar' === $phpType) {
6191
            if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6192
                $elementNS = " xmlns=\"$ns\"";
6193
            } else {
6194
                if ($unqualified) {
6195
                    $elementNS = ' xmlns=""';
6196
                } else {
6197
                    $elementNS = '';
6198
                }
6199
            }
6200
            if ('literal' === $use) {
6201
                if ($forceType) {
6202
                    $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6203
                } else {
6204
                    $xml = "<$name$elementNS>$value</$name>";
6205
                }
6206
            } else {
6207
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6208
            }
6209
        }
6210
        $this->debug("in serializeType: returning: $xml");
6211
6212
        return $xml;
6213
    }
6214
6215
    /**
6216
     * Serializes the attributes for a complexType
6217
     *
6218
     * @param array  $typeDef our internal representation of an XML schema type (or element)
6219
     * @param mixed  $value   a native PHP value (parameter value)
6220
     * @param string $ns      the namespace of the type
6221
     * @param string $uqType  the local part of the type
6222
     * @return string value serialized as an XML string
6223
     */
6224
    private function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType)
6225
    {
6226
        $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6227
        $xml = '';
6228
        if (isset($typeDef['extensionBase'])) {
6229
            $nsx     = $this->getPrefix($typeDef['extensionBase']);
6230
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6231
            if ($this->getNamespaceFromPrefix($nsx)) {
0 ignored issues
show
Bug introduced by
It seems like $nsx can also be of type false; however, parameter $prefix of Nusoap_base::getNamespaceFromPrefix() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6231
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6232
                $nsx = $this->getNamespaceFromPrefix($nsx);
6233
            }
6234
            if (false !== ($typeDefx = $this->getTypeDef($uqTypex, $nsx))) {
6235
                $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6236
                $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6237
            } else {
6238
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6239
            }
6240
        }
6241
        if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6242
            $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6243
            if (is_array($value)) {
6244
                $xvalue = $value;
6245
            } elseif (is_object($value)) {
6246
                $xvalue = get_object_vars($value);
6247
            } else {
6248
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6249
                $xvalue = [];
6250
            }
6251
            foreach ($typeDef['attrs'] as $aName => $attrs) {
6252
                if (isset($xvalue['!' . $aName])) {
6253
                    $xname = '!' . $aName;
6254
                    $this->debug("value provided for attribute $aName with key $xname");
6255
                } elseif (isset($xvalue[$aName])) {
6256
                    $xname = $aName;
6257
                    $this->debug("value provided for attribute $aName with key $xname");
6258
                } elseif (isset($attrs['default'])) {
6259
                    $xname          = '!' . $aName;
6260
                    $xvalue[$xname] = $attrs['default'];
6261
                    $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6262
                } else {
6263
                    $xname = '';
6264
                    $this->debug("no value provided for attribute $aName");
6265
                }
6266
                if ($xname) {
6267
                    $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . '"';
6268
                }
6269
            }
6270
        } else {
6271
            $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6272
        }
6273
6274
        return $xml;
6275
    }
6276
6277
    /**
6278
     * Serializes the elements for a complexType
6279
     *
6280
     * @param array       $typeDef       our internal representation of an XML schema type (or element)
6281
     * @param mixed       $value         a native PHP value (parameter value)
6282
     * @param string      $ns            the namespace of the type
6283
     * @param string      $uqType        the local part of the type
6284
     * @param string      $use           use for part (encoded|literal)
6285
     * @param bool|string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6286
     * @return string value serialized as an XML string
6287
     */
6288
    private function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use = 'encoded', $encodingStyle = false)
6289
    {
6290
        $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6291
        $xml = '';
6292
        if (isset($typeDef['extensionBase'])) {
6293
            $nsx     = $this->getPrefix($typeDef['extensionBase']);
6294
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6295
            if ($this->getNamespaceFromPrefix($nsx)) {
0 ignored issues
show
Bug introduced by
It seems like $nsx can also be of type false; however, parameter $prefix of Nusoap_base::getNamespaceFromPrefix() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6295
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6296
                $nsx = $this->getNamespaceFromPrefix($nsx);
6297
            }
6298
            if (false !== ($typeDefx = $this->getTypeDef($uqTypex, $nsx))) {
6299
                $this->debug("serialize elements for extension base $nsx:$uqTypex");
6300
                $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
6301
            } else {
6302
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6303
            }
6304
        }
6305
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6306
            $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6307
            if (is_array($value)) {
6308
                $xvalue = $value;
6309
            } elseif (is_object($value)) {
6310
                $xvalue = get_object_vars($value);
6311
            } else {
6312
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6313
                $xvalue = [];
6314
            }
6315
            // toggle whether all elements are present - ideally should validate against schema
6316
            if (is_array($xvalue) && (count($typeDef['elements']) !== count($xvalue))) {
6317
                $optionals = true;
6318
            }
6319
            foreach ($typeDef['elements'] as $eName => $attrs) {
6320
                if (!isset($xvalue[$eName])) {
6321
                    if (isset($attrs['default'])) {
6322
                        $xvalue[$eName] = $attrs['default'];
6323
                        $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6324
                    }
6325
                }
6326
                // if user took advantage of a minOccurs=0, then only serialize named parameters
6327
                if (isset($optionals) && !isset($xvalue[$eName]) && (!isset($attrs['nillable']) || 'true' !== $attrs['nillable'])) {
6328
                    if (isset($attrs['minOccurs']) && '0' !== $attrs['minOccurs']) {
6329
                        $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6330
                    }
6331
                    // do nothing
6332
                    $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6333
                } else {
6334
                    // get value
6335
                    $v = null;
6336
                    if (isset($xvalue[$eName])) {
6337
                        $v = $xvalue[$eName];
6338
                    }
6339
                    $unqualified = false;
6340
                    if (isset($attrs['form'])) {
6341
                        $unqualified = ('unqualified' === $attrs['form']);
6342
                    }
6343
                    if (isset($attrs['maxOccurs']) && ('unbounded' === $attrs['maxOccurs'] || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && 'arraySimple' === $this->isArraySimpleOrStruct($v)) {
6344
                        $vv = $v;
6345
                        foreach ($vv as $k => $v) {
6346
                            if (isset($attrs['type']) || isset($attrs['ref'])) {
6347
                                // serialize schema-defined type
6348
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6349
                            } else {
6350
                                // serialize generic type (can this ever really happen?)
6351
                                $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6352
                                $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6353
                            }
6354
                        }
6355
                    } else {
6356
                        if (null === $v && isset($attrs['minOccurs']) && '0' === $attrs['minOccurs']) {
6357
                            // do nothing
6358
                        } elseif (null === $v && isset($attrs['nillable']) && 'true' === $attrs['nillable']) {
6359
                            // TODO: serialize a nil correctly, but for now serialize schema-defined type
6360
                            $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6361
                        } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
6362
                            // serialize schema-defined type
6363
                            $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6364
                        } else {
6365
                            // serialize generic type (can this ever really happen?)
6366
                            $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6367
                            $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6368
                        }
6369
                    }
6370
                }
6371
            }
6372
        } else {
6373
            $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6374
        }
6375
6376
        return $xml;
6377
    }
6378
6379
    /**
6380
     * Adds an XML Schema complex type to the WSDL types
6381
     *
6382
     * @param string $name
6383
     * @param string $typeClass       (complexType|simpleType|attribute)
6384
     * @param string $phpType         currently supported are array and struct (php assoc array)
6385
     * @param string $compositor      (all|sequence|choice)
6386
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6387
     * @param array  $elements        e.g. array ( name => array(name=>'',type=>'') )
6388
     * @param array  $attrs           e.g. array(array('ref'=>'SOAP-ENC:arrayType','Wsdl:arrayType'=>'xsd:string[]'))
6389
     * @param string $arrayType       as namespace:name (xsd:string)
6390
     * @see    Nusoap_xmlschema
6391
     */
6392
    public function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = [], $attrs = [], $arrayType = '')
6393
    {
6394
        if (is_array($elements) && count($elements) > 0) {
6395
            $eElements = [];
6396
            foreach ($elements as $n => $e) {
6397
                // expand each element
6398
                $ee = [];
6399
                foreach ($e as $k => $v) {
6400
                    $k      = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
6401
                    $v      = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
6402
                    $ee[$k] = $v;
6403
                }
6404
                $eElements[$n] = $ee;
6405
            }
6406
            $elements = $eElements;
6407
        }
6408
6409
        if (is_array($attrs) && count($attrs) > 0) {
6410
            foreach ($attrs as $n => $a) {
6411
                // expand each attribute
6412
                foreach ($a as $k => $v) {
6413
                    $k      = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
6414
                    $v      = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
6415
                    $aa[$k] = $v;
6416
                }
6417
                $eAttrs[$n] = $aa;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $aa does not seem to be defined for all execution paths leading up to this point.
Loading history...
6418
            }
6419
            $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 6410. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
6420
        }
6421
6422
        $restrictionBase = mb_strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6423
        $arrayType       = mb_strpos($arrayType, ':') ? $this->expandQname($arrayType) : $arrayType;
6424
        $typens          = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6425
        $this->schemas[$typens][0]->addComplexType($name, $typeClass, $phpType, $compositor, $restrictionBase, $elements, $attrs, $arrayType);
6426
    }
6427
6428
    /**
6429
     * Adds an XML Schema simple type to the WSDL types
6430
     *
6431
     * @param string $name
6432
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6433
     * @param string $typeClass       (should always be simpleType)
6434
     * @param string $phpType         (should always be scalar)
6435
     * @param array  $enumeration     array of values
6436
     * @see    Nusoap_xmlschema
6437
     */
6438
    public function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = [])
6439
    {
6440
        $restrictionBase = mb_strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6441
        $typens          = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6442
        $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6443
    }
6444
6445
    /**
6446
     * Adds an element to the WSDL types
6447
     *
6448
     * @param array $attrs attributes that must include name and type
6449
     * @see    Nusoap_xmlschema
6450
     */
6451
    public function addElement($attrs)
6452
    {
6453
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6454
        $this->schemas[$typens][0]->addElement($attrs);
6455
    }
6456
6457
    /**
6458
     * Register an operation with the server
6459
     *
6460
     * @param string      $name          operation (method) name
6461
     * @param bool|array  $in            assoc array of input values: key = param name, value = param type
6462
     * @param bool|array  $out           assoc array of output values: key = param name, value = param type
6463
     * @param bool|string $namespace     optional The namespace for the operation
6464
     * @param bool|string $soapaction    optional The soapaction for the operation
6465
     * @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
6466
     * @param string      $use           (encoded|literal) optional The use for the parameters (cannot mix right now)
6467
     * @param string      $documentation optional The description to include in the WSDL
6468
     * @param string      $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6469
     * @return bool
6470
     */
6471
    public function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = '')
6472
    {
6473
        if ('encoded' === $use && '' === $encodingStyle) {
6474
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6475
        }
6476
6477
        if ('document' === $style) {
6478
            $elements = [];
6479
            foreach ($in as $n => $t) {
6480
                $elements[$n] = ['name' => $n, 'type' => $t, 'form' => 'unqualified'];
6481
            }
6482
            $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6483
            $this->addElement(['name' => $name, 'type' => $name . 'RequestType']);
6484
            $in       = ['parameters' => 'tns:' . $name . '^'];
6485
            $elements = [];
6486
            foreach ($out as $n => $t) {
6487
                $elements[$n] = ['name' => $n, 'type' => $t, 'form' => 'unqualified'];
6488
            }
6489
            $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6490
            $this->addElement(['name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified']);
6491
            $out = ['parameters' => 'tns:' . $name . 'Response' . '^'];
6492
        }
6493
6494
        // get binding
6495
        $this->bindings[$this->serviceName . 'Binding']['operations'][$name] = [
6496
            'name'          => $name,
6497
            'binding'       => $this->serviceName . 'Binding',
6498
            'endpoint'      => $this->endpoint,
6499
            'soapAction'    => $soapaction,
6500
            'style'         => $style,
6501
            'input'         => [
6502
                'use'           => $use,
6503
                'namespace'     => $namespace,
6504
                'encodingStyle' => $encodingStyle,
6505
                'message'       => $name . 'Request',
6506
                'parts'         => $in,
6507
            ],
6508
            'output'        => [
6509
                'use'           => $use,
6510
                'namespace'     => $namespace,
6511
                'encodingStyle' => $encodingStyle,
6512
                'message'       => $name . 'Response',
6513
                'parts'         => $out,
6514
            ],
6515
            'namespace'     => $namespace,
6516
            'transport'     => 'http://schemas.xmlsoap.org/soap/http',
6517
            'documentation' => $documentation,
6518
        ];
6519
        // add portTypes
6520
        // add messages
6521
        if ($in) {
6522
            foreach ($in as $pName => $pType) {
6523
                if (mb_strpos($pType, ':')) {
6524
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ':' . $this->getLocalPart($pType);
6525
                }
6526
                $this->messages[$name . 'Request'][$pName] = $pType;
6527
            }
6528
        } else {
6529
            $this->messages[$name . 'Request'] = '0';
6530
        }
6531
        if ($out) {
6532
            foreach ($out as $pName => $pType) {
6533
                if (mb_strpos($pType, ':')) {
6534
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ':' . $this->getLocalPart($pType);
6535
                }
6536
                $this->messages[$name . 'Response'][$pName] = $pType;
6537
            }
6538
        } else {
6539
            $this->messages[$name . 'Response'] = '0';
6540
        }
6541
6542
        return true;
6543
    }
6544
}
6545
6546
/**
6547
 * Nusoap_parser class parses SOAP XML messages into native PHP values
6548
 *
6549
 * @author   Dietrich Ayala <[email protected]>
6550
 * @author   Scott Nichol <[email protected]>
6551
 */
6552
class Nusoap_parser extends Nusoap_base
6553
{
6554
    public $xml                   = '';
6555
    public $xml_encoding          = '';
6556
    public $method                = '';
6557
    public $root_struct           = '';
6558
    public $root_struct_name      = '';
6559
    public $root_struct_namespace = '';
6560
    public $root_header           = '';
6561
    public $document              = '';            // incoming SOAP body (text)
6562
    // determines where in the message we are (envelope,header,body,method)
6563
    public $status            = '';
6564
    public $position          = 0;
6565
    public $depth             = 0;
6566
    public $default_namespace = '';
6567
    //    public $namespaces = []; //Field 'namespaces' is already defined in \Nusoap_base
6568
    public $message         = [];
6569
    public $parent          = '';
6570
    public $fault           = false;
6571
    public $fault_code      = '';
6572
    public $fault_str       = '';
6573
    public $fault_detail    = '';
6574
    public $depth_array     = [];
6575
    public $debug_flag      = true;
6576
    public $soapresponse;    // parsed SOAP Body
6577
    public $soapheader;        // parsed SOAP Header
6578
    public $responseHeaders = '';    // incoming SOAP headers (text)
6579
    public $body_position   = 0;
6580
    // for multiref parsing:
6581
    // array of id => pos
6582
    public $ids = [];
6583
    // array of id => hrefs => pos
6584
    public $multirefs = [];
6585
    // toggle for auto-decoding element content
6586
    public $decode_utf8 = true;
6587
6588
    /**
6589
     * Constructor that actually does the parsing
6590
     *
6591
     * @param    string       $xml         SOAP message
6592
     * @param    string       $encoding    character encoding scheme of message
6593
     * @param    string|array $method      method for which XML is parsed (unused?)
6594
     * @param    bool|string  $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6595
     */
6596
    public function __construct($xml, $encoding = 'UTF-8', $method = '', $decode_utf8 = true)
6597
    {
6598
        parent::__construct();
6599
        $this->xml          = $xml;
6600
        $this->xml_encoding = $encoding;
6601
        $this->method       = $method;
6602
        $this->decode_utf8  = $decode_utf8;
6603
        // Check whether content has been read.
6604
        if (!empty($xml)) {
6605
            // Check XML encoding
6606
            $pos_xml = mb_strpos($xml, '<?xml');
6607
            if (false !== $pos_xml) {
6608
                $xml_decl = mb_substr($xml, $pos_xml, mb_strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6609
                if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6610
                    $xml_encoding = $res[1];
6611
                    if (mb_strtoupper($xml_encoding) !== $encoding) {
6612
                        $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6613
                        $this->debug($err);
6614
                        if ('ISO-8859-1' !== $encoding || 'UTF-8' !== mb_strtoupper($xml_encoding)) {
6615
                            $this->setError($err);
6616
6617
                            return;
6618
                        }
6619
                        // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6620
                    } else {
6621
                        $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6622
                    }
6623
                } else {
6624
                    $this->debug('No encoding specified in XML declaration');
6625
                }
6626
            } else {
6627
                $this->debug('No XML declaration');
6628
            }
6629
            $this->debug('Entering Nusoap_parser(), length=' . mb_strlen($xml) . ', encoding=' . $encoding);
6630
            // Create an XML parser - why not xml_parser_create_ns?
6631
            $this->parser = xml_parser_create($this->xml_encoding);
0 ignored issues
show
Bug Best Practice introduced by
The property parser does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
6632
            // Set the options for parsing the XML data.
6633
            //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6634
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6635
            xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6636
            // Set the object for the parser.
6637
            xml_set_object($this->parser, $this);
6638
            // Set the element handlers for the parser.
6639
            xml_set_element_handler($this->parser, 'start_element', 'end_element');
6640
            xml_set_character_data_handler($this->parser, 'character_data');
6641
            // Parse the XML file.
6642
            if (!xml_parse($this->parser, $xml, true)) {
6643
                // Display an error message.
6644
                $err = sprintf('XML error parsing SOAP payload on line %d: %s', xml_get_current_line_number($this->parser), xml_error_string(xml_get_error_code($this->parser)));
6645
                $this->debug($err);
6646
                $this->debug("XML payload:\n" . $xml);
6647
                $this->setError($err);
6648
            } else {
6649
                $this->debug('in Nusoap_parser ctor, message:');
6650
                $this->appendDebug($this->varDump($this->message));
6651
                $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
6652
                // get final value
6653
                $this->soapresponse = $this->message[$this->root_struct]['result'];
6654
                // get header value
6655
                if ('' !== $this->root_header && isset($this->message[$this->root_header]['result'])) {
6656
                    $this->soapheader = $this->message[$this->root_header]['result'];
6657
                }
6658
                // resolve hrefs/ids
6659
                if (is_array($this->multirefs) && count($this->multirefs) > 0) {
6660
                    foreach ($this->multirefs as $id => $hrefs) {
6661
                        $this->debug('resolving multirefs for id: ' . $id);
6662
                        $idVal = $this->buildVal($this->ids[$id]);
6663
                        if (is_array($idVal) && isset($idVal['!id'])) {
6664
                            unset($idVal['!id']);
6665
                        }
6666
                        foreach ($hrefs as $refPos => $ref) {
6667
                            $this->debug('resolving href at pos ' . $refPos);
6668
                            $this->multirefs[$id][$refPos] = $idVal;
6669
                        }
6670
                    }
6671
                }
6672
            }
6673
            xml_parser_free($this->parser);
6674
            unset($this->parser);
6675
        } else {
6676
            $this->debug('xml was empty, didn\'t parse!');
6677
            $this->setError('xml was empty, didn\'t parse!');
6678
        }
6679
    }
6680
6681
    /**
6682
     * Start-element handler
6683
     *
6684
     * @param    resource $parser XML parser object
6685
     * @param    string   $name   element name
6686
     * @param    array    $attrs  associative array of attributes
6687
     */
6688
    private function start_element($parser, $name, $attrs)
0 ignored issues
show
Unused Code introduced by
The method start_element() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

6688
    private function start_element(/** @scrutinizer ignore-unused */ $parser, $name, $attrs)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
6689
    {
6690
        // position in a total number of elements, starting from 0
6691
        // update class level pos
6692
        $pos = $this->position++;
6693
        // and set mine
6694
        $this->message[$pos] = ['pos' => $pos, 'children' => '', 'cdata' => ''];
6695
        // depth = how many levels removed from root?
6696
        // set mine as current global depth and increment global depth value
6697
        $this->message[$pos]['depth'] = $this->depth++;
6698
        // else add self as child to whoever the current parent is
6699
        if (0 !== $pos) {
6700
            $this->message[$this->parent]['children'] .= '|' . $pos;
6701
        }
6702
        // set my parent
6703
        $this->message[$pos]['parent'] = $this->parent;
6704
        // set self as current parent
6705
        $this->parent = $pos;
6706
        // set self as current value for this depth
6707
        $this->depth_array[$this->depth] = $pos;
6708
        // get element prefix
6709
        if (mb_strpos($name, ':')) {
6710
            // get ns prefix
6711
            $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
6712
            // get unqualified name
6713
            $name = mb_substr(mb_strstr($name, ':'), 1);
6714
        }
6715
        // set status
6716
        if ('Envelope' === $name && '' === $this->status) {
6717
            $this->status = 'envelope';
6718
        } elseif ('Header' === $name && 'envelope' === $this->status) {
6719
            $this->root_header = $pos;
6720
            $this->status      = 'header';
6721
        } elseif ('Body' === $name && 'envelope' === $this->status) {
6722
            $this->status        = 'body';
6723
            $this->body_position = $pos;
6724
        // set method
6725
        } elseif ('body' === $this->status && $pos === ($this->body_position + 1)) {
6726
            $this->status                = 'method';
6727
            $this->root_struct_name      = $name;
6728
            $this->root_struct           = $pos;
6729
            $this->message[$pos]['type'] = 'struct';
6730
            $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6731
        }
6732
        // set my status
6733
        $this->message[$pos]['status'] = $this->status;
6734
        // set name
6735
        $this->message[$pos]['name'] = htmlspecialchars($name);
6736
        // set attrs
6737
        $this->message[$pos]['attrs'] = $attrs;
6738
        // loop through atts, logging ns and type declarations
6739
        $attstr = '';
6740
        foreach ($attrs as $key => $value) {
6741
            $key_prefix    = $this->getPrefix($key);
6742
            $key_localpart = $this->getLocalPart($key);
6743
            // if ns declarations, add to class level array of valid namespaces
6744
            if ('xmlns' === $key_prefix) {
6745
                if (preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) {
6746
                    $this->XMLSchemaVersion  = $value;
6747
                    $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6748
                    $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
6749
                }
6750
                $this->namespaces[$key_localpart] = $value;
6751
                // set method namespace
6752
                if ($name === $this->root_struct_name) {
6753
                    $this->methodNamespace = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property methodNamespace does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
6754
                }
6755
                // if it's a type declaration, set type
6756
            } elseif ('type' === $key_localpart) {
6757
                if (isset($this->message[$pos]['type']) && 'array' === $this->message[$pos]['type']) {
6758
                    // do nothing: already processed arrayType
6759
                } else {
6760
                    $value_prefix                      = $this->getPrefix($value);
6761
                    $value_localpart                   = $this->getLocalPart($value);
6762
                    $this->message[$pos]['type']       = $value_localpart;
6763
                    $this->message[$pos]['typePrefix'] = $value_prefix;
6764
                    if (isset($this->namespaces[$value_prefix])) {
6765
                        $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6766
                    } elseif (isset($attrs['xmlns:' . $value_prefix])) {
0 ignored issues
show
Bug introduced by
Are you sure $value_prefix of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6766
                    } elseif (isset($attrs['xmlns:' . /** @scrutinizer ignore-type */ $value_prefix])) {
Loading history...
6767
                        $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
6768
                    }
6769
                    // should do something here with the namespace of specified type?
6770
                }
6771
            } elseif ('arrayType' === $key_localpart) {
6772
                $this->message[$pos]['type'] = 'array';
6773
                /* do arrayType ereg here
6774
                [1]    arrayTypeValue    ::=    atype asize
6775
                [2]    atype    ::=    QName rank*
6776
                [3]    rank    ::=    '[' (',')* ']'
6777
                [4]    asize    ::=    '[' length~ ']'
6778
                [5]    length    ::=    nextDimension* Digit+
6779
                [6]    nextDimension    ::=    Digit+ ','
6780
                */
6781
                $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
6782
                if (preg_match($expr, $value, $regs)) {
6783
                    $this->message[$pos]['typePrefix']      = $regs[1];
6784
                    $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6785
                    if (isset($this->namespaces[$regs[1]])) {
6786
                        $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6787
                    } elseif (isset($attrs['xmlns:' . $regs[1]])) {
6788
                        $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:' . $regs[1]];
6789
                    }
6790
                    $this->message[$pos]['arrayType'] = $regs[2];
6791
                    $this->message[$pos]['arraySize'] = $regs[3];
6792
                    $this->message[$pos]['arrayCols'] = $regs[4];
6793
                }
6794
                // specifies nil value (or not)
6795
            } elseif ('nil' === $key_localpart) {
6796
                $this->message[$pos]['nil'] = ('true' === $value || '1' === $value);
6797
            // some other attribute
6798
            } elseif ('href' !== $key && 'xmlns' !== $key && 'encodingStyle' !== $key_localpart && 'root' !== $key_localpart) {
6799
                $this->message[$pos]['xattrs']['!' . $key] = $value;
6800
            }
6801
6802
            if ('xmlns' === $key) {
6803
                $this->default_namespace = $value;
6804
            }
6805
            // log id
6806
            if ('id' === $key) {
6807
                $this->ids[$value] = $pos;
6808
            }
6809
            // root
6810
            if ('root' === $key_localpart && 1 === $value) {
6811
                $this->status           = 'method';
6812
                $this->root_struct_name = $name;
6813
                $this->root_struct      = $pos;
6814
                $this->debug("found root struct $this->root_struct_name, pos $pos");
6815
            }
6816
            // for doclit
6817
            $attstr .= " $key=\"$value\"";
6818
        }
6819
        // get namespace - must be done after namespace atts are processed
6820
        if (isset($prefix)) {
6821
            $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6822
            $this->default_namespace          = $this->namespaces[$prefix];
6823
        } else {
6824
            $this->message[$pos]['namespace'] = $this->default_namespace;
6825
        }
6826
        if ('header' === $this->status) {
6827
            if ($this->root_header !== $pos) {
0 ignored issues
show
introduced by
The condition $this->root_header !== $pos is always true.
Loading history...
6828
                $this->responseHeaders .= '<' . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6829
            }
6830
        } elseif ('' !== $this->root_struct_name) {
6831
            $this->document .= '<' . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6832
        }
6833
    }
6834
6835
    /**
6836
     * End-element handler
6837
     *
6838
     * @param    resource $parser XML parser object
6839
     * @param    string   $name   element name
6840
     */
6841
    private function end_element($parser, $name)
0 ignored issues
show
Unused Code introduced by
The method end_element() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

6841
    private function end_element(/** @scrutinizer ignore-unused */ $parser, $name)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
6842
    {
6843
        // position of current element is equal to the last value left in depth_array for my depth
6844
        $pos = $this->depth_array[$this->depth--];
6845
        // get element prefix
6846
        if (mb_strpos($name, ':')) {
6847
            // get ns prefix
6848
            $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
6849
            // get unqualified name
6850
            $name = mb_substr(mb_strstr($name, ':'), 1);
6851
        }
6852
6853
        // build to native type
6854
        if (isset($this->body_position) && $pos > $this->body_position) {
6855
            // deal w/ multirefs
6856
            if (isset($this->message[$pos]['attrs']['href'])) {
6857
                // get id
6858
                $id = mb_substr($this->message[$pos]['attrs']['href'], 1);
6859
                // add placeholder to href array
6860
                $this->multirefs[$id][$pos] = 'placeholder';
6861
                // add set a reference to it as the result value
6862
                $this->message[$pos]['result'] = $this->multirefs[$id][$pos];
6863
            // build complexType values
6864
            } elseif ('' !== $this->message[$pos]['children']) {
6865
                // if result has already been generated (struct/array)
6866
                if (!isset($this->message[$pos]['result'])) {
6867
                    $this->message[$pos]['result'] = $this->buildVal($pos);
6868
                }
6869
                // build complexType values of attributes and possibly simpleContent
6870
            } elseif (isset($this->message[$pos]['xattrs'])) {
6871
                if (!empty($this->message[$pos]['nil'])) {
6872
                    $this->message[$pos]['xattrs']['!'] = null;
6873
                } elseif (isset($this->message[$pos]['cdata']) && '' !== trim($this->message[$pos]['cdata'])) {
6874
                    if (isset($this->message[$pos]['type'])) {
6875
                        $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'] : '');
6876
                    } else {
6877
                        $parent = $this->message[$pos]['parent'];
6878
                        if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
6879
                            $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6880
                        } else {
6881
                            $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6882
                        }
6883
                    }
6884
                }
6885
                $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6886
            // set value of simpleType (or nil complexType)
6887
            } else {
6888
                //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6889
                if (!empty($this->message[$pos]['nil'])) {
6890
                    $this->message[$pos]['xattrs']['!'] = null;
6891
                } elseif (isset($this->message[$pos]['type'])) {
6892
                    $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'] : '');
6893
                } else {
6894
                    $parent = $this->message[$pos]['parent'];
6895
                    if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
6896
                        $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6897
                    } else {
6898
                        $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6899
                    }
6900
                }
6901
6902
                /* add value to parent's result, if parent is struct/array
6903
                $parent = $this->message[$pos]['parent'];
6904
                if($this->message[$parent]['type'] != 'map'){
6905
                    if(strtolower($this->message[$parent]['type']) == 'array'){
6906
                        $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6907
                    } else {
6908
                        $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6909
                    }
6910
                }
6911
                */
6912
            }
6913
        }
6914
6915
        // for doclit
6916
        if ('header' === $this->status) {
6917
            if ($this->root_header !== $pos) {
6918
                $this->responseHeaders .= '</' . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6919
            }
6920
        } elseif ($pos >= $this->root_struct) {
6921
            $this->document .= '</' . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6922
        }
6923
        // switch status
6924
        if ($pos === $this->root_struct) {
6925
            $this->status                = 'body';
6926
            $this->root_struct_namespace = $this->message[$pos]['namespace'];
6927
        } elseif ($pos === $this->root_header) {
6928
            $this->status = 'envelope';
6929
        } elseif ('Body' === $name && 'body' === $this->status) {
6930
            $this->status = 'envelope';
6931
        } elseif ('Header' === $name && 'header' === $this->status) {
6932
            // will never happen
6933
            $this->status = 'envelope';
6934
        } elseif ('Envelope' === $name && 'envelope' === $this->status) {
6935
            $this->status = '';
6936
        }
6937
        // set parent back to my parent
6938
        $this->parent = $this->message[$pos]['parent'];
6939
    }
6940
6941
    /**
6942
     * Element content handler
6943
     *
6944
     * @param    resource $parser XML parser object
6945
     * @param    string   $data   element content
6946
     */
6947
    private function character_data($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

6947
    private function character_data(/** @scrutinizer ignore-unused */ $parser, $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The method character_data() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
6948
    {
6949
        $pos = $this->depth_array[$this->depth];
6950
        if ('UTF-8' === $this->xml_encoding) {
6951
            // TODO: add an option to disable this for folks who want
6952
            // raw UTF-8 that, e.g., might not map to iso-8859-1
6953
            // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6954
            if ($this->decode_utf8) {
6955
                $data = utf8_decode($data);
6956
            }
6957
        }
6958
        $this->message[$pos]['cdata'] .= $data;
6959
        // for doclit
6960
        if ('header' === $this->status) {
6961
            $this->responseHeaders .= $data;
6962
        } else {
6963
            $this->document .= $data;
6964
        }
6965
    }
6966
6967
    /**
6968
     * Get the parsed message (SOAP Body)
6969
     *
6970
     * @return    mixed
6971
     * @deprecated    use get_soapbody instead
6972
     */
6973
    public function get_response()
6974
    {
6975
        return $this->soapresponse;
6976
    }
6977
6978
    /**
6979
     * Get the parsed SOAP Body (null if there was none)
6980
     *
6981
     * @return    mixed
6982
     */
6983
    public function get_soapbody()
6984
    {
6985
        return $this->soapresponse;
6986
    }
6987
6988
    /**
6989
     * Get the parsed SOAP Header (null if there was none)
6990
     *
6991
     * @return    mixed
6992
     */
6993
    public function get_soapheader()
6994
    {
6995
        return $this->soapheader;
6996
    }
6997
6998
    /**
6999
     * Get the unparsed SOAP Header
7000
     *
7001
     * @return    string XML or empty if no Header
7002
     */
7003
    public function getHeaders()
7004
    {
7005
        return $this->responseHeaders;
7006
    }
7007
7008
    /**
7009
     * Decodes simple types into PHP variables
7010
     *
7011
     * @param    string $value  value to decode
7012
     * @param    string $type   XML type to decode
7013
     * @param    string $typens XML type namespace to decode
7014
     * @return    mixed PHP value
7015
     */
7016
    private function decodeSimple($value, $type, $typens)
0 ignored issues
show
Unused Code introduced by
The parameter $typens is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

7016
    private function decodeSimple($value, $type, /** @scrutinizer ignore-unused */ $typens)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
7017
    {
7018
        // TODO: use the namespace!
7019
        if (!isset($type) || 'string' === $type || 'long' === $type || 'unsignedLong' === $type) {
7020
            return (string)$value;
7021
        }
7022
        if ('int' === $type || 'integer' === $type || 'short' === $type || 'byte' === $type) {
7023
            return (int)$value;
7024
        }
7025
        if ('float' === $type || 'double' === $type || 'decimal' === $type) {
7026
            return (float)$value;
7027
        }
7028
        if ('boolean' === $type) {
7029
            if ('false' === mb_strtolower($value) || 'f' === mb_strtolower($value)) {
7030
                return false;
7031
            }
7032
7033
            return (bool)$value;
7034
        }
7035
        if ('base64' === $type || 'base64Binary' === $type) {
7036
            $this->debug('Decode base64 value');
7037
7038
            return base64_decode($value, true);
7039
        }
7040
        // obscure numeric types
7041
        if ('nonPositiveInteger' === $type || 'negativeInteger' === $type || 'nonNegativeInteger' === $type || 'positiveInteger' === $type || 'unsignedInt' === $type || 'unsignedShort' === $type || 'unsignedByte' === $type) {
7042
            return (int)$value;
7043
        }
7044
        // bogus: parser treats array with no elements as a simple type
7045
        if ('array' === $type) {
7046
            return [];
7047
        }
7048
        // everything else
7049
        return (string)$value;
7050
    }
7051
7052
    /**
7053
     * Builds response structures for compound values (arrays/structs)
7054
     * and scalars
7055
     *
7056
     * @param int $pos position in node tree
7057
     * @return    mixed    PHP value
7058
     */
7059
    private function buildVal($pos)
7060
    {
7061
        if (!isset($this->message[$pos]['type'])) {
7062
            $this->message[$pos]['type'] = '';
7063
        }
7064
        $this->debug('in buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
7065
        // if there are children...
7066
        if ('' !== $this->message[$pos]['children']) {
7067
            $this->debug('in buildVal, there are children');
7068
            $children = explode('|', $this->message[$pos]['children']);
7069
            array_shift($children); // knock off empty
7070
            // md array
7071
            if (isset($this->message[$pos]['arrayCols']) && '' !== $this->message[$pos]['arrayCols']) {
7072
                $r = 0; // rowcount
7073
                $c = 0; // colcount
7074
                foreach ($children as $child_pos) {
7075
                    $this->debug("in buildVal, got an MD array element: $r, $c");
7076
                    $params[$r][] = $this->message[$child_pos]['result'];
7077
                    ++$c;
7078
                    if ($c === $this->message[$pos]['arrayCols']) {
7079
                        $c = 0;
7080
                        ++$r;
7081
                    }
7082
                }
7083
                // array
7084
            } elseif ('array' === $this->message[$pos]['type'] || 'Array' === $this->message[$pos]['type']) {
7085
                $this->debug('in buildVal, adding array ' . $this->message[$pos]['name']);
7086
                foreach ($children as $child_pos) {
7087
                    $params[] = &$this->message[$child_pos]['result'];
7088
                }
7089
                // apache Map type: java hashtable
7090
            } elseif ('Map' === $this->message[$pos]['type'] && 'http://xml.apache.org/xml-soap' === $this->message[$pos]['type_namespace']) {
7091
                $this->debug('in buildVal, Java Map ' . $this->message[$pos]['name']);
7092
                foreach ($children as $child_pos) {
7093
                    $kv                                       = explode('|', $this->message[$child_pos]['children']);
7094
                    $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
7095
                }
7096
                // generic compound type
7097
                //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
7098
            } else {
7099
                // Apache Vector type: treat as an array
7100
                $this->debug('in buildVal, adding Java Vector or generic compound type ' . $this->message[$pos]['name']);
7101
                if ('Vector' === $this->message[$pos]['type'] && 'http://xml.apache.org/xml-soap' === $this->message[$pos]['type_namespace']) {
7102
                    $notstruct = 1;
7103
                } else {
7104
                    $notstruct = 0;
7105
                }
7106
7107
                foreach ($children as $child_pos) {
7108
                    if ($notstruct) {
7109
                        $params[] = &$this->message[$child_pos]['result'];
7110
                    } else {
7111
                        if (isset($params[$this->message[$child_pos]['name']])) {
7112
                            // de-serialize repeated element name into an array
7113
                            if ((!is_array($params[$this->message[$child_pos]['name']])) || !isset($params[$this->message[$child_pos]['name']][0])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $params seems to be defined later in this foreach loop on line 7109. Are you sure it is defined here?
Loading history...
7114
                                $params[$this->message[$child_pos]['name']] = [$params[$this->message[$child_pos]['name']]];
7115
                            }
7116
                            $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
7117
                        } else {
7118
                            $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
7119
                        }
7120
                    }
7121
                }
7122
            }
7123
            if (isset($this->message[$pos]['xattrs'])) {
7124
                $this->debug('in buildVal, handling attributes');
7125
                foreach ($this->message[$pos]['xattrs'] as $n => $v) {
7126
                    $params[$n] = $v;
7127
                }
7128
            }
7129
            // handle simpleContent
7130
            if (isset($this->message[$pos]['cdata']) && '' !== trim($this->message[$pos]['cdata'])) {
7131
                $this->debug('in buildVal, handling simpleContent');
7132
                if (isset($this->message[$pos]['type'])) {
7133
                    $params['!'] = $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']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7137
                        $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7138
                    } else {
7139
                        $params['!'] = $this->message[$pos]['cdata'];
7140
                    }
7141
                }
7142
            }
7143
            $ret = is_array($params) ? $params : [];
7144
            $this->debug('in buildVal, return:');
7145
            $this->appendDebug($this->varDump($ret));
7146
7147
            return $ret;
7148
        }
7149
7150
        $this->debug('in buildVal, no children, building scalar');
7151
        $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
7152
        if (isset($this->message[$pos]['type'])) {
7153
            $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7154
            $this->debug("in buildVal, return: $ret");
7155
7156
            return $ret;
7157
        }
7158
        $parent = $this->message[$pos]['parent'];
7159
        if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7160
            $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7161
            $this->debug("in buildVal, return: $ret");
7162
7163
            return $ret;
7164
        }
7165
        $ret = $this->message[$pos]['cdata'];
7166
        $this->debug("in buildVal, return: $ret");
7167
7168
        return $ret;
7169
    }
7170
}
7171
7172
/**
7173
 * Backward compatibility
7174
 */
7175
class Soap_parser extends Nusoap_parser
7176
{
7177
}
7178
7179
/**
7180
 * [nu]soapclient higher level class for easy usage.
7181
 *
7182
 * Usage:
7183
 *
7184
 * // instantiate client with server info
7185
 * $soapclient = new Nusoap_client( string path [ ,mixed wsdl] );
7186
 *
7187
 * // call method, get results
7188
 * echo $soapclient->call( string methodname [ ,array parameters] );
7189
 *
7190
 * // bye bye client
7191
 * unset($soapclient);
7192
 *
7193
 * @author   Dietrich Ayala <[email protected]>
7194
 * @author   Scott Nichol <[email protected]>
7195
 */
7196
class Nusoap_client extends Nusoap_base
7197
{
7198
    public $username             = '';                // Username for HTTP authentication
7199
    public $password             = '';                // Password for HTTP authentication
7200
    public $authtype             = '';                // Type of HTTP authentication
7201
    public $certRequest          = [];        // Certificate for HTTP SSL authentication
7202
    public $requestHeaders       = false;    // SOAP headers in request (text)
7203
    public $responseHeaders      = '';        // SOAP headers from response (incomplete namespace resolution) (text)
7204
    public $responseHeader;        // SOAP Header from response (parsed)
7205
    public $document             = '';                // SOAP body response portion (incomplete namespace resolution) (text)
7206
    public $endpoint;
7207
    public $forceEndpoint        = '';        // overrides WSDL endpoint
7208
    public $proxyhost            = '';
7209
    public $proxyport            = '';
7210
    public $proxyusername        = '';
7211
    public $proxypassword        = '';
7212
    public $portName             = '';                // port name to use in WSDL
7213
    public $xml_encoding         = '';            // character set encoding of incoming (response) messages
7214
    public $http_encoding        = false;
7215
    public $timeout              = 0;                // HTTP connection timeout
7216
    public $response_timeout     = 30;        // HTTP response timeout
7217
    public $endpointType         = '';            // soap|wsdl, empty for WSDL initialization error
7218
    public $persistentConnection = false;
7219
    public $defaultRpcParams     = false;    // This is no longer used
7220
    public $request              = '';                // HTTP request
7221
    public $response             = '';                // HTTP response
7222
    public $responseData         = '';            // SOAP payload of response
7223
    public $cookies              = [];            // Cookies from response or for request
7224
    public $decode_utf8          = true;        // toggles whether the parser decodes element content w/ utf8_decode()
7225
    public $operations           = [];        // WSDL operations, empty for WSDL initialization error
7226
    public $curl_options         = [];    // User-specified cURL options
7227
    public $bindingType          = '';            // WSDL operation binding type
7228
    public $use_curl             = false;            // whether to always try to use cURL
7229
7230
    /*
7231
     * Fault related variables
7232
     */
7233
    /**
7234
     * @var  string|bool $fault
7235
     */
7236
    public $fault;
7237
    /**
7238
     * @var  string $faultcode
7239
     */
7240
    public $faultcode;
7241
    /**
7242
     * @var string $faultstring
7243
     */
7244
    public $faultstring;
7245
    /**
7246
     * @var string $faultdetail
7247
     */
7248
    public $faultdetail;
7249
7250
    /**
7251
     * Constructor
7252
     *
7253
     * @param    mixed       $endpoint         SOAP server or WSDL URL (string), or Wsdl instance (object)
7254
     * @param    mixed       $wsdl             optional, set to 'Wsdl' or true if using WSDL
7255
     * @param    bool|string $proxyhost        optional
7256
     * @param    bool|string $proxyport        optional
7257
     * @param    bool|string $proxyusername    optional
7258
     * @param    bool|string $proxypassword    optional
7259
     * @param int            $timeout          set the connection timeout
7260
     * @param int            $response_timeout set the response timeout
7261
     * @param    string      $portName         optional portName in WSDL document
7262
     */
7263
    public function __construct($endpoint, $wsdl = false, $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = '')
7264
    {
7265
        parent::__construct();
7266
        $this->endpoint         = $endpoint;
7267
        $this->proxyhost        = $proxyhost;
7268
        $this->proxyport        = $proxyport;
7269
        $this->proxyusername    = $proxyusername;
7270
        $this->proxypassword    = $proxypassword;
7271
        $this->timeout          = $timeout;
7272
        $this->response_timeout = $response_timeout;
7273
        $this->portName         = $portName;
7274
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7275
        $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7276
        // make values
7277
        if ($wsdl) {
7278
            if (is_object($endpoint) && ('Wsdl' === get_class($endpoint))) {
7279
                $this->wsdl     = $endpoint;
0 ignored issues
show
Bug Best Practice introduced by
The property wsdl does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
7280
                $this->endpoint = $this->wsdl->wsdl;
7281
                $this->wsdlFile = $this->endpoint;
0 ignored issues
show
Bug Best Practice introduced by
The property wsdlFile does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
7282
                $this->debug('existing Wsdl instance created from ' . $this->endpoint);
7283
                $this->checkWSDL();
7284
            } else {
7285
                $this->wsdlFile = $this->endpoint;
7286
                $this->wsdl     = null;
7287
                $this->debug('will use lazy evaluation of Wsdl from ' . $this->endpoint);
7288
            }
7289
            $this->endpointType = 'Wsdl';
7290
        } else {
7291
            $this->debug("instantiate SOAP with endpoint at $endpoint");
7292
            $this->endpointType = 'soap';
7293
        }
7294
    }
7295
7296
    /**
7297
     * Calls method, returns PHP native type
7298
     *
7299
     * @param    string $operation  SOAP server URL or path
7300
     * @param    mixed  $params     An array, associative or simple, of the parameters
7301
     *                              for the method call, or a string that is the XML
7302
     *                              for the call.  For rpc style, this call will
7303
     *                              wrap the XML in a tag named after the method, as
7304
     *                              well as the SOAP Envelope and Body.  For document
7305
     *                              style, this will only wrap with the Envelope and Body.
7306
     *                              IMPORTANT: when using an array with document style,
7307
     *                              in which case there
7308
     *                              is really one parameter, the root of the fragment
7309
     *                              used in the call, which encloses what programmers
7310
     *                              normally think of parameters.  A parameter array
7311
     *                              *must* include the wrapper.
7312
     * @param    string $namespace  optional method namespace (WSDL can override)
7313
     * @param    string $soapAction optional SOAPAction value (WSDL can override)
7314
     * @param    mixed  $headers    optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7315
     * @param    bool   $rpcParams  optional (no longer used)
7316
     * @param    string $style      optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7317
     * @param    string $use        optional (encoded|literal) the use when serializing parameters (WSDL can override)
7318
     * @return    mixed    response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
7319
     */
7320
    public function call($operation, $params = [], $namespace = 'http://tempuri.org', $soapAction = '', $headers = false, $rpcParams = null, $style = 'rpc', $use = 'encoded')
7321
    {
7322
        $this->operation = $operation;
0 ignored issues
show
Bug Best Practice introduced by
The property operation does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
7323
        $this->fault     = false;
7324
        $this->setError('');
7325
        $this->request      = '';
7326
        $this->response     = '';
7327
        $this->responseData = '';
7328
        $this->faultstring  = '';
7329
        $this->faultcode    = '';
7330
        $this->opData       = [];
0 ignored issues
show
Bug Best Practice introduced by
The property opData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
7331
        $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7332
        $this->appendDebug('params=' . $this->varDump($params));
7333
        $this->appendDebug('headers=' . $this->varDump($headers));
7334
        if ($headers) {
7335
            $this->requestHeaders = $headers;
7336
        }
7337
        if ('Wsdl' === $this->endpointType && null === $this->wsdl) {
7338
            $this->loadWSDL();
7339
            if ($this->getError()) {
7340
                return false;
7341
            }
7342
        }
7343
        // serialize parameters
7344
        if ('Wsdl' === $this->endpointType && $opData = $this->getOperationData($operation)) {
7345
            // use WSDL for operation
7346
            $this->opData = $opData;
7347
            $this->debug('found operation');
7348
            $this->appendDebug('opData=' . $this->varDump($opData));
7349
            if (isset($opData['soapAction'])) {
7350
                $soapAction = $opData['soapAction'];
7351
            }
7352
            if (!$this->forceEndpoint) {
7353
                $this->endpoint = $opData['endpoint'];
7354
            } else {
7355
                $this->endpoint = $this->forceEndpoint;
7356
            }
7357
            $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7358
            $style     = $opData['style'];
7359
            $use       = $opData['input']['use'];
7360
            // add ns to ns array
7361
            if ('' !== $namespace && !isset($this->wsdl->namespaces[$namespace])) {
7362
                $nsPrefix                          = 'ns' . mt_rand(1000, 9999);
7363
                $this->wsdl->namespaces[$nsPrefix] = $namespace;
7364
            }
7365
            $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7366
            // serialize payload
7367
            if (is_string($params)) {
7368
                $this->debug("serializing param string for WSDL operation $operation");
7369
                $payload = $params;
7370
            } elseif (is_array($params)) {
7371
                $this->debug("serializing param array for WSDL operation $operation");
7372
                $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params, $this->bindingType);
7373
            } else {
7374
                $this->debug('params must be array or string');
7375
                $this->setError('params must be array or string');
7376
7377
                return false;
7378
            }
7379
            $usedNamespaces = $this->wsdl->usedNamespaces;
7380
            $encodingStyle  = '';
7381
            if (isset($opData['input']['encodingStyle'])) {
7382
                $encodingStyle = $opData['input']['encodingStyle'];
7383
            }
7384
            $this->appendDebug($this->wsdl->getDebug());
7385
            $this->wsdl->clearDebug();
7386
            if (false !== ($errstr = $this->wsdl->getError())) {
7387
                $this->debug('got wsdl error: ' . $errstr);
7388
                $this->setError('wsdl error: ' . $errstr);
7389
7390
                return false;
7391
            }
7392
        } elseif ('Wsdl' === $this->endpointType) {
7393
            // operation not in WSDL
7394
            $this->appendDebug($this->wsdl->getDebug());
7395
            $this->wsdl->clearDebug();
7396
            $this->setError('operation ' . $operation . ' not present in WSDL.');
7397
            $this->debug("operation '$operation' not present in WSDL.");
7398
7399
            return false;
7400
        } else {
7401
            // no WSDL
7402
            //$this->namespaces['ns1'] = $namespace;
7403
            $nsPrefix = 'ns' . mt_rand(1000, 9999);
7404
            // serialize
7405
            $payload = '';
7406
            if (is_string($params)) {
7407
                $this->debug("serializing param string for operation $operation");
7408
                $payload = $params;
7409
            } elseif (is_array($params)) {
7410
                $this->debug("serializing param array for operation $operation");
7411
                foreach ($params as $k => $v) {
7412
                    $payload .= $this->serialize_val($v, $k, false, false, false, false, $use);
7413
                }
7414
            } else {
7415
                $this->debug('params must be array or string');
7416
                $this->setError('params must be array or string');
7417
7418
                return false;
7419
            }
7420
            $usedNamespaces = [];
7421
            $encodingStyle  = '';
7422
            if ('encoded' === $use) {
7423
                $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7424
            }
7425
        }
7426
        // wrap RPC calls with method element
7427
        if ('rpc' === $style) {
7428
            if ('literal' === $use) {
7429
                $this->debug('wrapping RPC request with literal method element');
7430
                if ($namespace) {
7431
                    // 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
7432
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . $payload . "</$nsPrefix:$operation>";
7433
                } else {
7434
                    $payload = "<$operation>" . $payload . "</$operation>";
7435
                }
7436
            } else {
7437
                $this->debug('wrapping RPC request with encoded method element');
7438
                if ($namespace) {
7439
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . $payload . "</$nsPrefix:$operation>";
7440
                } else {
7441
                    $payload = "<$operation>" . $payload . "</$operation>";
7442
                }
7443
            }
7444
        }
7445
        // serialize envelope
7446
        $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $usedNamespaces, $style, $use, $encodingStyle);
7447
        $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7448
        $this->debug('SOAP message length=' . mb_strlen($soapmsg) . ' contents (max 1000 bytes)=' . mb_substr($soapmsg, 0, 1000));
7449
        // send
7450
        $return = $this->send($this->getHTTPBody($soapmsg), $soapAction, $this->timeout, $this->response_timeout);
7451
        if (false !== ($errstr = $this->getError())) {
7452
            $this->debug('Error: ' . $errstr);
7453
7454
            return false;
7455
        }
7456
            $this->return = $return;
0 ignored issues
show
Bug Best Practice introduced by
The property return does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
7457
            $this->debug('sent message successfully and got a(n) ' . gettype($return));
7458
            $this->appendDebug('return=' . $this->varDump($return));
7459
            // fault?
7460
            if (is_array($return) && isset($return['faultcode'])) {
7461
                $this->debug('got fault');
7462
                $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
7463
                $this->fault = true;
7464
                foreach ($return as $k => $v) {
7465
                    $this->$k = $v;
7466
                    if (is_array($v)) {
7467
                        $this->debug("$k = " . json_encode($v));
7468
                    } else {
7469
                        $this->debug("$k = $v<br>");
7470
                    }
7471
                }
7472
7473
                return $return;
7474
            } elseif ('document' === $style) {
7475
                // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7476
                // we are only going to return the first part here...sorry about that
7477
                return $return;
7478
        }
7479
                // array of return values
7480
                if (is_array($return)) {
7481
                    // multiple 'out' parameters, which we return wrapped up
7482
                    // in the array
7483
                    if (is_array($return) && count($return) > 1) {
7484
                        return $return;
7485
                    }
7486
                    // single 'out' parameter (normally the return value)
7487
                    $return = array_shift($return);
7488
                    $this->debug('return shifted value: ');
7489
                    $this->appendDebug($this->varDump($return));
7490
7491
                    return $return;
7492
                // nothing returned (ie, echoVoid)
7493
        }
7494
7495
                    return '';
7496
                }
7497
7498
    /**
7499
     * Check WSDL passed as an instance or pulled from an endpoint
7500
     */
7501
    private function checkWSDL()
7502
    {
7503
        $this->appendDebug($this->wsdl->getDebug());
7504
        $this->wsdl->clearDebug();
7505
        $this->debug('checkWSDL');
7506
        // catch errors
7507
        if (false !== ($errstr = $this->wsdl->getError())) {
7508
            $this->appendDebug($this->wsdl->getDebug());
7509
            $this->wsdl->clearDebug();
7510
            $this->debug('got wsdl error: ' . $errstr);
7511
            $this->setError('wsdl error: ' . $errstr);
7512
        } elseif (false !== ($this->operations = $this->wsdl->getOperations($this->portName, 'soap'))) {
7513
            $this->appendDebug($this->wsdl->getDebug());
7514
            $this->wsdl->clearDebug();
7515
            $this->bindingType = 'soap';
7516
            $this->debug('got ' . count($this->operations) . ' operations from Wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7517
        } elseif (false !== ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12'))) {
7518
            $this->appendDebug($this->wsdl->getDebug());
7519
            $this->wsdl->clearDebug();
7520
            $this->bindingType = 'soap12';
7521
            $this->debug('got ' . count($this->operations) . ' operations from Wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7522
            $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7523
        } else {
7524
            $this->appendDebug($this->wsdl->getDebug());
7525
            $this->wsdl->clearDebug();
7526
            $this->debug('getOperations returned false');
7527
            $this->setError('no operations defined in the WSDL document!');
7528
        }
7529
    }
7530
7531
    /**
7532
     * Instantiate Wsdl object and parse Wsdl file
7533
     */
7534
    public function loadWSDL()
7535
    {
7536
        $this->debug('instantiating Wsdl class with doc: ' . $this->wsdlFile);
7537
        $this->wsdl = new Wsdl('', $this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword, $this->timeout, $this->response_timeout, $this->curl_options, $this->use_curl);
0 ignored issues
show
Bug Best Practice introduced by
The property wsdl does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
7538
        $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7539
        $this->wsdl->fetchWSDL($this->wsdlFile);
7540
        $this->checkWSDL();
7541
    }
7542
7543
    /**
7544
     * Get available data pertaining to an operation
7545
     *
7546
     * @param    string $operation operation name
7547
     * @return   bool|array array of data pertaining to the operation
7548
     */
7549
    public function getOperationData($operation)
7550
    {
7551
        if ('Wsdl' === $this->endpointType && null === $this->wsdl) {
7552
            $this->loadWSDL();
7553
            if ($this->getError()) {
7554
                return false;
7555
            }
7556
        }
7557
        if (isset($this->operations[$operation])) {
7558
            return $this->operations[$operation];
7559
        }
7560
        $this->debug("No data for operation: $operation");
7561
    }
7562
7563
    /**
7564
     * Send the SOAP message
7565
     *
7566
     * Note: if the operation has multiple return values
7567
     * the return value of this method will be an array
7568
     * of those values.
7569
     *
7570
     * @param    string $msg              a SOAPx4 soapmsg object
7571
     * @param    string $soapaction       SOAPAction value
7572
     * @param int       $timeout          set connection timeout in seconds
7573
     * @param int       $response_timeout set response timeout in seconds
7574
     * @return    mixed native PHP types.
7575
     */
7576
    private function send($msg, $soapaction = '', $timeout = 0, $response_timeout = 30)
7577
    {
7578
        $this->checkCookies();
7579
        // detect transport
7580
        switch (true) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/^http/', $this->endpoint) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
7581
            // http(s)
7582
            case preg_match('/^http/', $this->endpoint):
7583
                $this->debug('transporting via HTTP');
7584
                if (true === $this->persistentConnection && is_object($this->persistentConnection)) {
0 ignored issues
show
introduced by
The condition is_object($this->persistentConnection) is always false.
Loading history...
7585
                    $http = $this->persistentConnection;
7586
                } else {
7587
                    $http = new Soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7588
                    if ($this->persistentConnection) {
7589
                        $http->usePersistentConnection();
7590
                    }
7591
                }
7592
                $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7593
                $http->setSOAPAction($soapaction);
7594
                if ($this->proxyhost && $this->proxyport) {
7595
                    $http->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
7596
                }
7597
                if ('' !== $this->authtype) {
7598
                    $http->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
7599
                }
7600
                if ('' !== $this->http_encoding) {
0 ignored issues
show
introduced by
The condition '' !== $this->http_encoding is always true.
Loading history...
7601
                    $http->setEncoding($this->http_encoding);
7602
                }
7603
                $this->debug('sending message, length=' . mb_strlen($msg));
7604
                if (preg_match('/^http:/', $this->endpoint)) {
7605
                    //if(strpos($this->endpoint,'http:')){
7606
                    $this->responseData = $http->send($msg, $timeout, $response_timeout, $this->cookies);
7607
                } elseif (preg_match('/^https/', $this->endpoint)) {
7608
                    //} elseif(strpos($this->endpoint,'https:')){
7609
                    //if (PHP_VERSION == '4.3.0-dev') {
7610
                    //$response = $http->send($msg,$timeout,$response_timeout);
7611
                    //$this->request = $http->outgoing_payload;
7612
                    //$this->response = $http->incoming_payload;
7613
                    //} else
7614
                    $this->responseData = $http->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
0 ignored issues
show
Deprecated Code introduced by
The function Soap_transport_http::sendHTTPS() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

7614
                    $this->responseData = /** @scrutinizer ignore-deprecated */ $http->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
Loading history...
7615
                } else {
7616
                    $this->setError('no http/s in endpoint url');
7617
                }
7618
                $this->request  = $http->outgoing_payload;
7619
                $this->response = $http->incoming_payload;
7620
                $this->appendDebug($http->getDebug());
7621
                $this->UpdateCookies($http->incoming_cookies);
7622
                // save transport object if using persistent connections
7623
                if ($this->persistentConnection) {
7624
                    $http->clearDebug();
7625
                    if (!is_object($this->persistentConnection)) {
0 ignored issues
show
introduced by
The condition is_object($this->persistentConnection) is always false.
Loading history...
7626
                        $this->persistentConnection = $http;
7627
                    }
7628
                }
7629
7630
                if (false !== ($err = $http->getError())) {
7631
                    $this->setError('HTTP Error: ' . $err);
7632
7633
                    return false;
7634
                } elseif ($this->getError()) {
7635
                    return false;
7636
                }
7637
                $this->debug('got response, length=' . mb_strlen($this->responseData) . ' type=' . $http->incoming_headers['content-type']);
7638
7639
                return $this->parseResponse($http->incoming_headers, $this->responseData);
7640
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
7641
            default:
7642
                $this->setError('no transport found, or selected transport is not yet supported!');
7643
7644
                return false;
7645
                break;
7646
        }
7647
    }
7648
7649
    /**
7650
     * Processes SOAP message returned from server
7651
     *
7652
     * @param    array  $headers The HTTP headers
7653
     * @param    string $data    unprocessed response data from server
7654
     * @return    mixed    value of the message, decoded into a PHP type
7655
     */
7656
    private function parseResponse($headers, $data)
7657
    {
7658
        $this->debug('Entering parseResponse() for data of length ' . mb_strlen($data) . ' headers:');
7659
        $this->appendDebug($this->varDump($headers));
7660
        if (!isset($headers['content-type'])) {
7661
            $this->setError('Response not of type text/xml (no content-type header)');
7662
7663
            return false;
7664
        }
7665
        if (false === mb_strpos($headers['content-type'], 'text/xml')) {
7666
            $this->setError('Response not of type text/xml: ' . $headers['content-type']);
7667
7668
            return false;
7669
        }
7670
        if (mb_strpos($headers['content-type'], '=')) {
7671
            $enc = str_replace('"', '', mb_substr(mb_strstr($headers['content-type'], '='), 1));
7672
            $this->debug('Got response encoding: ' . $enc);
7673
            $this->xml_encoding = 'US-ASCII';
7674
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
7675
                $this->xml_encoding = mb_strtoupper($enc);
7676
            }
7677
        } else {
7678
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
7679
            $this->xml_encoding = 'ISO-8859-1';
7680
        }
7681
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating Nusoap_parser');
7682
        $parser = new Nusoap_parser($data, $this->xml_encoding, $this->operations, $this->decode_utf8);
7683
        // add parser debug data to our debug
7684
        $this->appendDebug($parser->getDebug());
7685
        // if parse errors
7686
        if (false !== ($errstr = $parser->getError())) {
7687
            $this->setError($errstr);
7688
            // destroy the parser object
7689
            unset($parser);
7690
7691
            return false;
7692
        }
7693
            // get SOAP headers
7694
            $this->responseHeaders = $parser->getHeaders();
7695
            // get SOAP headers
7696
            $this->responseHeader = $parser->get_soapheader();
7697
            // get decoded message
7698
            $return = $parser->get_soapbody();
7699
            // add document for doclit support
7700
            $this->document = $parser->document;
7701
            // destroy the parser object
7702
            unset($parser);
7703
            // return decode message
7704
            return $return;
7705
        }
7706
7707
    /**
7708
     * Sets user-specified cURL options
7709
     *
7710
     * @param    mixed $option The cURL option (always integer?)
7711
     * @param    mixed $value  The cURL option value
7712
     */
7713
    public function setCurlOption($option, $value)
7714
    {
7715
        $this->debug("setCurlOption option=$option, value=");
7716
        $this->appendDebug($this->varDump($value));
7717
        $this->curl_options[$option] = $value;
7718
    }
7719
7720
    /**
7721
     * Sets the SOAP endpoint, which can override WSDL
7722
     *
7723
     * @param    string $endpoint The endpoint URL to use, or empty string or false to prevent override
7724
     */
7725
    public function setEndpoint($endpoint)
7726
    {
7727
        $this->debug("setEndpoint(\"$endpoint\")");
7728
        $this->forceEndpoint = $endpoint;
7729
    }
7730
7731
    /**
7732
     * Set the SOAP headers
7733
     *
7734
     * @param    mixed $headers String of XML with SOAP header content, or array of Soapval objects for SOAP headers
7735
     */
7736
    public function setHeaders($headers)
7737
    {
7738
        $this->debug('setHeaders headers=');
7739
        $this->appendDebug($this->varDump($headers));
7740
        $this->requestHeaders = $headers;
7741
    }
7742
7743
    /**
7744
     * Get the SOAP response headers (namespace resolution incomplete)
7745
     *
7746
     * @return    string
7747
     */
7748
    public function getHeaders()
7749
    {
7750
        return $this->responseHeaders;
7751
    }
7752
7753
    /**
7754
     * Get the SOAP response Header (parsed)
7755
     *
7756
     * @return    mixed
7757
     */
7758
    public function getHeader()
7759
    {
7760
        return $this->responseHeader;
7761
    }
7762
7763
    /**
7764
     * Set proxy info here
7765
     *
7766
     * @param    string $proxyhost
7767
     * @param    string $proxyport
7768
     * @param    string $proxyusername
7769
     * @param    string $proxypassword
7770
     */
7771
    public function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
7772
    {
7773
        $this->proxyhost     = $proxyhost;
7774
        $this->proxyport     = $proxyport;
7775
        $this->proxyusername = $proxyusername;
7776
        $this->proxypassword = $proxypassword;
7777
    }
7778
7779
    /**
7780
     * If authenticating, set user credentials here
7781
     *
7782
     * @param    string $username
7783
     * @param    string $password
7784
     * @param    string $authtype    (basic|digest|certificate|ntlm)
7785
     * @param    array  $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
7786
     */
7787
    public function setCredentials($username, $password, $authtype = 'basic', $certRequest = [])
7788
    {
7789
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
7790
        $this->appendDebug($this->varDump($certRequest));
7791
        $this->username    = $username;
7792
        $this->password    = $password;
7793
        $this->authtype    = $authtype;
7794
        $this->certRequest = $certRequest;
7795
    }
7796
7797
    /**
7798
     * Use HTTP encoding
7799
     *
7800
     * @param    string $enc HTTP encoding
7801
     */
7802
    public function setHTTPEncoding($enc = 'gzip, deflate')
7803
    {
7804
        $this->debug("setHTTPEncoding(\"$enc\")");
7805
        $this->http_encoding = $enc;
7806
    }
7807
7808
    /**
7809
     * Set whether to try to use cURL connections if possible
7810
     *
7811
     * @param    bool $use Whether to try to use cURL
7812
     */
7813
    public function setUseCURL($use)
7814
    {
7815
        $this->debug("setUseCURL($use)");
7816
        $this->use_curl = $use;
7817
    }
7818
7819
    /**
7820
     * Use HTTP persistent connections if possible
7821
     */
7822
    public function useHTTPPersistentConnection()
7823
    {
7824
        $this->debug('useHTTPPersistentConnection');
7825
        $this->persistentConnection = true;
7826
    }
7827
7828
    /**
7829
     * Gets the default RPC parameter setting.
7830
     * If true, default is that call params are like RPC even for document style.
7831
     * Each call() can override this value.
7832
     *
7833
     * This is no longer used.
7834
     *
7835
     * @return bool
7836
     * @deprecated
7837
     */
7838
    public function getDefaultRpcParams()
7839
    {
7840
        return $this->defaultRpcParams;
7841
    }
7842
7843
    /**
7844
     * Sets the default RPC parameter setting.
7845
     * If true, default is that call params are like RPC even for document style
7846
     * Each call() can override this value.
7847
     *
7848
     * This is no longer used.
7849
     *
7850
     * @param    bool $rpcParams
7851
     * @deprecated
7852
     */
7853
    public function setDefaultRpcParams($rpcParams)
7854
    {
7855
        $this->defaultRpcParams = $rpcParams;
7856
    }
7857
7858
    /**
7859
     * Dynamically creates an instance of a proxy class,
7860
     * allowing user to directly call methods from Wsdl
7861
     *
7862
     * @return   object soap_proxy object
7863
     */
7864
    public function getProxy()
7865
    {
7866
        $r       = mt_rand();
7867
        $evalStr = $this->_getProxyClassCode($r);
7868
        //$this->debug("proxy class: $evalStr");
7869
        if ($this->getError()) {
7870
            $this->debug('Error from _getProxyClassCode, so return null');
7871
7872
            return null;
7873
        }
7874
        // eval the class
7875
        eval($evalStr);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
7876
        // instantiate proxy object
7877
        eval("\$proxy = new Nusoap_proxy_$r('');");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
7878
        // transfer current Wsdl data to the proxy thereby avoiding parsing the Wsdl twice
7879
        $proxy->endpointType = 'Wsdl';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $proxy seems to be never defined.
Loading history...
7880
        $proxy->wsdlFile         = $this->wsdlFile;
7881
        $proxy->wsdl             = $this->wsdl;
7882
        $proxy->operations       = $this->operations;
7883
        $proxy->defaultRpcParams = $this->defaultRpcParams;
7884
        // transfer other state
7885
        $proxy->soap_defencoding     = $this->soap_defencoding;
7886
        $proxy->username             = $this->username;
7887
        $proxy->password             = $this->password;
7888
        $proxy->authtype             = $this->authtype;
7889
        $proxy->certRequest          = $this->certRequest;
7890
        $proxy->requestHeaders       = $this->requestHeaders;
7891
        $proxy->endpoint             = $this->endpoint;
7892
        $proxy->forceEndpoint        = $this->forceEndpoint;
7893
        $proxy->proxyhost            = $this->proxyhost;
7894
        $proxy->proxyport            = $this->proxyport;
7895
        $proxy->proxyusername        = $this->proxyusername;
7896
        $proxy->proxypassword        = $this->proxypassword;
7897
        $proxy->http_encoding        = $this->http_encoding;
7898
        $proxy->timeout              = $this->timeout;
7899
        $proxy->response_timeout     = $this->response_timeout;
7900
        $proxy->persistentConnection = $this->persistentConnection;
7901
        $proxy->decode_utf8          = $this->decode_utf8;
7902
        $proxy->curl_options         = $this->curl_options;
7903
        $proxy->bindingType          = $this->bindingType;
7904
        $proxy->use_curl             = $this->use_curl;
7905
7906
        return $proxy;
7907
    }
7908
7909
    /**
7910
     * Dynamically creates proxy class code
7911
     *
7912
     * @param string $r
7913
     * @return   string PHP/NuSOAP code for the proxy class
7914
     */
7915
    private function _getProxyClassCode($r)
7916
    {
7917
        $this->debug("in getProxy endpointType=$this->endpointType");
7918
        $this->appendDebug('Wsdl=' . $this->varDump($this->wsdl));
7919
        if ('Wsdl' !== $this->endpointType) {
7920
            $evalStr = 'A proxy can only be created for a WSDL client';
7921
            $this->setError($evalStr);
7922
            $evalStr = "echo \"$evalStr\";";
7923
7924
            return $evalStr;
7925
        }
7926
        if ('Wsdl' === $this->endpointType && null === $this->wsdl) {
7927
            $this->loadWSDL();
7928
            if ($this->getError()) {
7929
                return 'echo "' . $this->getError() . '";';
7930
            }
7931
        }
7932
        $evalStr = '';
7933
        foreach ($this->operations as $operation => $opData) {
7934
            if ('' !== $operation) {
7935
                // create param string and param comment string
7936
                if (is_array($opData['input']['parts']) && count($opData['input']['parts']) > 0) {
7937
                    $paramStr        = '';
7938
                    $paramArrayStr   = '';
7939
                    $paramCommentStr = '';
7940
                    foreach ($opData['input']['parts'] as $name => $type) {
7941
                        $paramStr        .= "\$$name, ";
7942
                        $paramArrayStr   .= "'$name' => \$$name, ";
7943
                        $paramCommentStr .= "$type \$$name, ";
7944
                    }
7945
                    $paramStr        = mb_substr($paramStr, 0, -2);
7946
                    $paramArrayStr   = mb_substr($paramArrayStr, 0, -2);
7947
                    $paramCommentStr = mb_substr($paramCommentStr, 0, -2);
7948
                } else {
7949
                    $paramStr        = '';
7950
                    $paramArrayStr   = '';
7951
                    $paramCommentStr = 'void';
7952
                }
7953
                $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
7954
                $evalStr             .= "// $paramCommentStr
7955
	function " . str_replace('.', '__', $operation) . "($paramStr) {
7956
		\$params = array($paramArrayStr);
7957
		return \$this->call('$operation', \$params, '" . $opData['namespace'] . "', '" . (isset($opData['soapAction']) ? $opData['soapAction'] : '') . "');
7958
	}
7959
	";
7960
                unset($paramStr, $paramCommentStr);
7961
            }
7962
        }
7963
        $evalStr = 'class Nusoap_proxy_' . $r . ' extends Nusoap_client {
7964
	' . $evalStr . '
7965
}';
7966
7967
        return $evalStr;
7968
    }
7969
7970
    /**
7971
     * Dynamically creates proxy class code
7972
     *
7973
     * @return   string PHP/NuSOAP code for the proxy class
7974
     */
7975
    public function getProxyClassCode()
7976
    {
7977
        $r = mt_rand();
7978
7979
        return $this->_getProxyClassCode($r);
7980
    }
7981
7982
    /**
7983
     * Gets the HTTP body for the current request.
7984
     *
7985
     * @param string $soapmsg The SOAP payload
7986
     * @return string The HTTP body, which includes the SOAP payload
7987
     */
7988
    private function getHTTPBody($soapmsg)
7989
    {
7990
        return $soapmsg;
7991
    }
7992
7993
    /**
7994
     * Gets the HTTP content type for the current request.
7995
     *
7996
     * Note: getHTTPBody must be called before this.
7997
     *
7998
     * @return string the HTTP content type for the current request.
7999
     */
8000
    private function getHTTPContentType()
8001
    {
8002
        return 'text/xml';
8003
    }
8004
8005
    /**
8006
     * Gets the HTTP content type charset for the current request.
8007
     * Returns false for non-text content types.
8008
     *
8009
     * Note: getHTTPBody must be called before this.
8010
     *
8011
     * @return string the HTTP content type charset for the current request.
8012
     */
8013
    private function getHTTPContentTypeCharset()
8014
    {
8015
        return $this->soap_defencoding;
8016
    }
8017
8018
    /*
8019
    * Whether or not parser should decode utf8 element content
8020
    *
8021
    * @return   always returns true
8022
    * @access   public
8023
    */
8024
8025
    /**
8026
     * @param $bool
8027
     * @return bool
8028
     */
8029
    public function decodeUTF8($bool)
8030
    {
8031
        $this->decode_utf8 = $bool;
8032
8033
        return true;
8034
    }
8035
8036
    /**
8037
     * Adds a new Cookie into $this->cookies array
8038
     *
8039
     * @param    string $name  Cookie Name
8040
     * @param    string $value Cookie Value
8041
     * @return bool if cookie-set was successful returns true, else false
8042
     */
8043
    public function setCookie($name, $value)
8044
    {
8045
        if ('' === $name) {
8046
            return false;
8047
        }
8048
        $this->cookies[] = ['name' => $name, 'value' => $value];
8049
8050
        return true;
8051
    }
8052
8053
    /**
8054
     * Gets all Cookies
8055
     *
8056
     * @return   array with all internal cookies
8057
     */
8058
    public function getCookies()
8059
    {
8060
        return $this->cookies;
8061
    }
8062
8063
    /**
8064
     * Checks all Cookies and delete those which are expired
8065
     *
8066
     * @return   bool always return true
8067
     */
8068
    private function checkCookies()
8069
    {
8070
        if (0 === count($this->cookies)) {
8071
            return true;
8072
        }
8073
        $this->debug('checkCookie: check ' . count($this->cookies) . ' cookies');
8074
        $curr_cookies  = $this->cookies;
8075
        $this->cookies = [];
8076
        foreach ($curr_cookies as $cookie) {
8077
            if (!is_array($cookie)) {
8078
                $this->debug('Remove cookie that is not an array');
8079
                continue;
8080
            }
8081
            if (isset($cookie['expires']) && !empty($cookie['expires'])) {
8082
                if (strtotime($cookie['expires']) > time()) {
8083
                    $this->cookies[] = $cookie;
8084
                } else {
8085
                    $this->debug('Remove expired cookie ' . $cookie['name']);
8086
                }
8087
            } else {
8088
                $this->cookies[] = $cookie;
8089
            }
8090
        }
8091
        $this->debug('checkCookie: ' . count($this->cookies) . ' cookies left in array');
8092
8093
        return true;
8094
    }
8095
8096
    /**
8097
     * Updates the current cookies with a new set
8098
     *
8099
     * @param    array $cookies new cookies with which to update current ones
8100
     * @return bool always return true
8101
     */
8102
    private function UpdateCookies($cookies)
8103
    {
8104
        if (0 === count($this->cookies)) {
8105
            // no existing cookies: take whatever is new
8106
            if (is_array($cookies) && count($cookies) > 0) {
8107
                $this->debug('Setting new cookie(s)');
8108
                $this->cookies = $cookies;
8109
            }
8110
8111
            return true;
8112
        }
8113
        if (0 === count($cookies)) {
8114
            // no new cookies: keep what we've got
8115
            return true;
8116
        }
8117
        // merge
8118
        foreach ($cookies as $newCookie) {
8119
            if (!is_array($newCookie)) {
8120
                continue;
8121
            }
8122
            if (!isset($newCookie['name']) || !isset($newCookie['value'])) {
8123
                continue;
8124
            }
8125
            $newName = $newCookie['name'];
8126
            $found   = false;
8127
            for ($i = 0, $iMax = count($this->cookies); $i < $iMax; ++$i) {
8128
                $cookie = $this->cookies[$i];
8129
                if (!is_array($cookie)) {
8130
                    continue;
8131
                }
8132
                if (!isset($cookie['name'])) {
8133
                    continue;
8134
                }
8135
                if ($newName !== $cookie['name']) {
8136
                    continue;
8137
                }
8138
                $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
8139
                $domain    = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
8140
                if ($newDomain !== $domain) {
8141
                    continue;
8142
                }
8143
                $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
8144
                $path    = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
8145
                if ($newPath !== $path) {
8146
                    continue;
8147
                }
8148
                $this->cookies[$i] = $newCookie;
8149
                $found             = true;
8150
                $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
8151
                break;
8152
            }
8153
            if (!$found) {
8154
                $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
8155
                $this->cookies[] = $newCookie;
8156
            }
8157
        }
8158
8159
        return true;
8160
    }
8161
}
8162
8163
if (!extension_loaded('soap')) {
8164
    /**
8165
     *    For backwards compatiblity, define Soapclient unless the PHP SOAP extension is loaded.
8166
     */
8167
    class Soapclient extends Nusoap_client
8168
    {
8169
    }
8170
}
8171