Passed
Branch feature/php-cs-fixer (60ba9d)
by Michael
03:35
created

Soap_transport_http::getResponse()   F

Complexity

Conditions 81
Paths 3167

Size

Total Lines 369
Code Lines 241

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 81
eloc 241
c 1
b 0
f 0
nc 3167
nop 0
dl 0
loc 369
rs 2

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
//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
613
                $valueType = $this->isArraySimpleOrStruct($val);
614
                if ('arraySimple' === $valueType || preg_match('/^ArrayOf/', $type)) {
615
                    $this->debug('serialize_val: serialize array');
616
                    $i = 0;
617
                    if (is_array($val) && count($val) > 0) {
618
                        foreach ($val as $v) {
619
                            if (is_object($v) && 'soapval' === get_class($v)) {
620
                                $tt_ns = $v->type_ns;
621
                                $tt    = $v->type;
622
                            } elseif (is_array($v)) {
623
                                $tt = $this->isArraySimpleOrStruct($v);
624
                            } else {
625
                                $tt = gettype($v);
626
                            }
627
                            $array_types[$tt] = 1;
628
                            // TODO: for literal, the name should be $name
629
                            $xml .= $this->serialize_val($v, 'item', false, false, false, false, $use);
630
                            ++$i;
631
                        }
632
                        if (is_array($array_types) && count($array_types) > 1) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $array_types seems to be defined by a foreach iteration on line 618. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
633
                            $array_typename = 'xsd:anyType';
634
                        } elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
635
                            if ('integer' === $tt) {
636
                                $tt = 'int';
637
                            }
638
                            $array_typename = 'xsd:' . $tt;
639
                        } elseif (isset($tt) && 'arraySimple' === $tt) {
640
                            $array_typename = 'SOAP-ENC:Array';
641
                        } elseif (isset($tt) && 'arrayStruct' === $tt) {
642
                            $array_typename = 'unnamed_struct_use_soapval';
643
                        } else {
644
                            // if type is prefixed, create type prefix
645
                            if ('' !== $tt_ns && $tt_ns === $this->namespaces['xsd']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tt_ns does not seem to be defined for all execution paths leading up to this point.
Loading history...
646
                                $array_typename = 'xsd:' . $tt;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tt seems to be defined by a foreach iteration on line 618. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
647
                            } elseif ($tt_ns) {
648
                                $tt_prefix      = 'ns' . mt_rand(1000, 9999);
649
                                $array_typename = "$tt_prefix:$tt";
650
                                $xmlns          .= " xmlns:$tt_prefix=\"$tt_ns\"";
651
                            } else {
652
                                $array_typename = $tt;
653
                            }
654
                        }
655
                        $array_type = $i;
656
                        if ('literal' === $use) {
657
                            $type_str = '';
658
                        } elseif (isset($type) && isset($type_prefix)) {
659
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
660
                        } else {
661
                            $type_str = ' xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="' . $array_typename . "[$array_type]\"";
662
                        }
663
                        // empty array
664
                    } else {
665
                        $type_str = ' xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:anyType[0]"';
666
                        if ('literal' === $use) {
667
                            $type_str = '';
668
                        } elseif (isset($type) && isset($type_prefix)) {
669
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
670
                        }
671
                    }
672
                    // TODO: for array in literal, there is no wrapper here
673
                    $xml = "<$name$xmlns$type_str$atts>" . $xml . "</$name>";
674
                } else {
675
                    // got a struct
676
                    $this->debug('serialize_val: serialize struct');
677
                    $type_str = '';
678
                    if (isset($type) && isset($type_prefix)) {
679
                        $type_str = " xsi:type=\"$type_prefix:$type\"";
680
                    }
681
                    if ('literal' === $use) {
682
                        $xml .= "<$name$xmlns$atts>";
683
                    } else {
684
                        $xml .= "<$name$xmlns$type_str$atts>";
685
                    }
686
                    foreach ($val as $k => $v) {
687
                        // Apache Map
688
                        if ('Map' === $type && 'http://xml.apache.org/xml-soap' === $type_ns) {
689
                            $xml .= '<item>';
690
                            $xml .= $this->serialize_val($k, 'key', false, false, false, false, $use);
691
                            $xml .= $this->serialize_val($v, 'value', false, false, false, false, $use);
692
                            $xml .= '</item>';
693
                        } else {
694
                            $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
695
                        }
696
                    }
697
                    $xml .= "</$name>";
698
                }
699
700
                break;
701
            default:
702
                $this->debug('serialize_val: serialize unknown');
703
                $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
704
705
                break;
706
        }
707
        $this->debug("serialize_val returning $xml");
708
709
        return $xml;
710
    }
711
712
    /**
713
     * Serializes a message
714
     *
715
     * @param string $body          the XML of the SOAP body
716
     * @param mixed  $headers       optional string of XML with SOAP header content, or array of Soapval objects for SOAP headers, or associative array
717
     * @param array  $namespaces    optional the namespaces used in generating the body and headers
718
     * @param string $style         optional (rpc|document)
719
     * @param string $use           optional (encoded|literal)
720
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
721
     * @return string the message
722
     */
723
    public function serializeEnvelope($body, $headers = false, $namespaces = [], $style = 'rpc', $use = 'encoded', $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/')
724
    {
725
        // TODO: add an option to automatically run utf8_encode on $body and $headers
726
        // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
727
        // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
728
729
        $this->debug('In serializeEnvelope length=' . mb_strlen($body) . ' body (max 1000 characters)=' . mb_substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
730
        $this->debug('headers:');
731
        $this->appendDebug($this->varDump($headers));
732
        $this->debug('namespaces:');
733
        $this->appendDebug($this->varDump($namespaces));
734
        // serialize namespaces
735
        $ns_string = '';
736
        foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
737
            $ns_string .= " xmlns:$k=\"$v\"";
738
        }
739
        if ($encodingStyle) {
740
            $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
741
        }
742
743
        // serialize headers
744
        if ($headers) {
745
            if (is_array($headers)) {
746
                $xml = '';
747
                foreach ($headers as $k => $v) {
748
                    if (is_object($v) && 'soapval' === get_class($v)) {
749
                        $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
750
                    } else {
751
                        $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
752
                    }
753
                }
754
                $headers = $xml;
755
                $this->debug("In serializeEnvelope, serialized array of headers to $headers");
756
            }
757
            $headers = '<SOAP-ENV:Header>' . $headers . '</SOAP-ENV:Header>';
758
        }
759
        // serialize envelope
760
        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

760
        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...
761
    }
762
763
    /**
764
     * Formats a string to be inserted into an HTML stream
765
     *
766
     * @param string $str The string to format
767
     * @return string The formatted string
768
     * @deprecated
769
     */
770
    public function formatDump($str)
771
    {
772
        $str = htmlspecialchars($str);
773
774
        return nl2br($str);
775
    }
776
777
    /**
778
     * Contracts (changes namespace to prefix) a qualified name
779
     *
780
     * @param    string $qname qname
781
     * @return    string contracted qname
782
     */
783
    protected function contractQName($qname)
784
    {
785
        // get element namespace
786
        //$this->xdebug("Contract $qname");
787
        if (mb_strrpos($qname, ':')) {
788
            // get unqualified name
789
            $name = mb_substr($qname, mb_strrpos($qname, ':') + 1);
790
            // get ns
791
            $ns = mb_substr($qname, 0, mb_strrpos($qname, ':'));
792
            $p  = $this->getPrefixFromNamespace($ns);
793
            if ($p) {
794
                return $p . ':' . $name;
795
            }
796
797
            return $qname;
798
        }
799
800
        return $qname;
801
    }
802
803
    /**
804
     * Expands (changes prefix to namespace) a qualified name
805
     *
806
     * @param    string $qname qname
807
     * @return    string expanded qname
808
     */
809
    protected function expandQname($qname)
810
    {
811
        // get element prefix
812
        if (mb_strpos($qname, ':') && !preg_match('/^http:\/\//', $qname)) {
813
            // get unqualified name
814
            $name = mb_substr(mb_strstr($qname, ':'), 1);
815
            // get ns prefix
816
            $prefix = mb_substr($qname, 0, mb_strpos($qname, ':'));
817
            if (isset($this->namespaces[$prefix])) {
818
                return $this->namespaces[$prefix] . ':' . $name;
819
            }
820
821
                return $qname;
822
            }
823
824
            return $qname;
825
        }
826
827
    /**
828
     * Returns the local part of a prefixed string
829
     * Returns the original string, if not prefixed
830
     *
831
     * @param string $str The prefixed string
832
     * @return string The local part
833
     */
834
    public function getLocalPart($str)
835
    {
836
        if (false !== ($sstr = mb_strrchr($str, ':'))) {
837
            // get unqualified name
838
            return mb_substr($sstr, 1);
839
        }
840
841
            return $str;
842
        }
843
844
    /**
845
     * Returns the prefix part of a prefixed string
846
     * Returns false, if not prefixed
847
     *
848
     * @param string $str The prefixed string
849
     * @return mixed The prefix or false if there is no prefix
850
     */
851
    public function getPrefix($str)
852
    {
853
        if (false !== ($pos = mb_strrpos($str, ':'))) {
854
            // get prefix
855
            return mb_substr($str, 0, $pos);
856
        }
857
858
        return false;
859
    }
860
861
    /**
862
     * Pass it a prefix, it returns a namespace
863
     *
864
     * @param string $prefix The prefix
865
     * @return mixed The namespace, false if no namespace has the specified prefix
866
     */
867
    public function getNamespaceFromPrefix($prefix)
868
    {
869
        if (isset($this->namespaces[$prefix])) {
870
            return $this->namespaces[$prefix];
871
        }
872
        //$this->setError("No namespace registered for prefix '$prefix'");
873
        return false;
874
    }
875
876
    /**
877
     * Returns the prefix for a given namespace (or prefix)
878
     * or false if no prefixes registered for the given namespace
879
     *
880
     * @param string $ns The namespace
881
     * @return mixed The prefix, false if the namespace has no prefixes
882
     */
883
    public function getPrefixFromNamespace($ns)
884
    {
885
        foreach ($this->namespaces as $p => $n) {
886
            if ($ns === $n || $ns === $p) {
887
                $this->usedNamespaces[$p] = $n;
888
889
                return $p;
890
            }
891
        }
892
893
        return false;
894
    }
895
896
    /**
897
     * Returns the time in ODBC canonical form with microseconds
898
     *
899
     * @return string The time in ODBC canonical form with microseconds
900
     */
901
    public function getmicrotime()
902
    {
903
        if (function_exists('gettimeofday')) {
904
            $tod  = gettimeofday();
905
            $sec  = $tod['sec'];
906
            $usec = $tod['usec'];
907
        } else {
908
            $sec  = time();
909
            $usec = 0;
910
        }
911
912
        return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
913
    }
914
915
    /**
916
     * Returns a string with the output of var_dump
917
     *
918
     * @param mixed $data The variable to var_dump
919
     * @return string The output of var_dump
920
     */
921
    public function varDump($data)
922
    {
923
        ob_start();
924
        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...
925
        $ret_val = ob_get_contents();
926
        ob_end_clean();
927
928
        return $ret_val;
929
    }
930
931
    /**
932
     * Represents the object as a string
933
     *
934
     * @return    string
935
     */
936
    public function __toString()
937
    {
938
        return $this->varDump($this);
939
    }
940
}
941
942
// XML Schema Datatype Helper Functions
943
944
//xsd:dateTime helpers
945
946
/**
947
 * Convert unix timestamp to ISO 8601 compliant date string
948
 *
949
 * @param    int  $timestamp Unix time stamp
950
 * @param    bool $utc       Whether the time stamp is UTC or local
951
 * @return    mixed ISO 8601 date string or false
952
 */
953
function timestamp_to_iso8601($timestamp, $utc = true)
954
{
955
    $datestr = date('Y-m-d\TH:i:sO', $timestamp);
956
    $pos     = mb_strrpos($datestr, '+');
957
    if (false === $pos) {
958
        $pos = mb_strrpos($datestr, '-');
959
    }
960
    if (false !== $pos) {
961
        if (mb_strlen($datestr) === $pos + 5) {
962
            $datestr = mb_substr($datestr, 0, $pos + 3) . ':' . mb_substr($datestr, -2);
963
        }
964
    }
965
    if ($utc) {
966
        $pattern = '/' . '([0-9]{4})-' .    // centuries & years CCYY-
967
                   '([0-9]{2})-' .    // months MM-
968
                   '([0-9]{2})' .    // days DD
969
                   'T' .            // separator T
970
                   '([0-9]{2}):' .    // hours hh:
971
                   '([0-9]{2}):' .    // minutes mm:
972
                   '([0-9]{2})(\.[0-9]*)?' . // seconds ss.ss...
973
                   '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
974
                   '/';
975
        if (preg_match($pattern, $datestr, $regs)) {
976
            return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6]);
977
        }
978
979
        return false;
980
    }
981
982
        return $datestr;
983
    }
984
985
/**
986
 * Convert ISO 8601 compliant date string to unix timestamp
987
 *
988
 * @param    string $datestr ISO 8601 compliant date string
989
 * @return    mixed Unix timestamp (int) or false
990
 */
991
function iso8601_to_timestamp($datestr)
992
{
993
    $pattern = '/' . '([0-9]{4})-' .    // centuries & years CCYY-
994
               '([0-9]{2})-' .    // months MM-
995
               '([0-9]{2})' .    // days DD
996
               'T' .            // separator T
997
               '([0-9]{2}):' .    // hours hh:
998
               '([0-9]{2}):' .    // minutes mm:
999
               '([0-9]{2})(\.[0-9]+)?' . // seconds ss.ss...
1000
               '(Z|[+\-][0-9]{2}:?[0-9]{2})?' . // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
1001
               '/';
1002
    if (preg_match($pattern, $datestr, $regs)) {
1003
        // not utc
1004
        if ('Z' !== $regs[8]) {
1005
            $op = mb_substr($regs[8], 0, 1);
1006
            $h  = mb_substr($regs[8], 1, 2);
1007
            $m  = mb_substr($regs[8], mb_strlen($regs[8]) - 2, 2);
1008
            if ('-' === $op) {
1009
                $regs[4] += $h;
1010
                $regs[5] += $m;
1011
            } elseif ('+' === $op) {
1012
                $regs[4] -= $h;
1013
                $regs[5] -= $m;
1014
            }
1015
        }
1016
1017
        return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1018
    //		return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
1019
    }
1020
1021
        return false;
1022
    }
1023
1024
/**
1025
 * Sleeps some number of microseconds
1026
 *
1027
 * @param    string $usec the number of microseconds to sleep
1028
 * @deprecated
1029
 */
1030
function usleepWindows($usec)
1031
{
1032
    $start = gettimeofday();
1033
    do {
1034
        $stop       = gettimeofday();
1035
        $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + $stop['usec'] - $start['usec'];
1036
    } while ($timePassed < $usec);
1037
}
1038
1039
/**
1040
 * Contains information for a SOAP fault.
1041
 * Mainly used for returning faults from deployed functions
1042
 * in a server instance.
1043
 * @author   Dietrich Ayala <[email protected]>
1044
 */
1045
class Nusoap_fault extends Nusoap_base
1046
{
1047
    /**
1048
     * The fault code (client|server)
1049
     * @var string
1050
     */
1051
    private $faultcode;
1052
    /**
1053
     * The fault actor
1054
     * @var string
1055
     */
1056
    private $faultactor;
1057
    /**
1058
     * The fault string, a description of the fault
1059
     * @var string
1060
     */
1061
    private $faultstring;
1062
    /**
1063
     * The fault detail, typically a string or array of string
1064
     * @var mixed
1065
     */
1066
    private $faultdetail;
1067
1068
    /**
1069
     * Constructor
1070
     *
1071
     * @param string $faultcode   (SOAP-ENV:Client | SOAP-ENV:Server)
1072
     * @param string $faultactor  only used when msg routed between multiple actors
1073
     * @param string $faultstring human readable error message
1074
     * @param mixed  $faultdetail detail, typically a string or array of string
1075
     */
1076
    public function __construct($faultcode, $faultactor = '', $faultstring = '', $faultdetail = '')
1077
    {
1078
        parent::__construct();
1079
        $this->faultcode   = $faultcode;
1080
        $this->faultactor  = $faultactor;
1081
        $this->faultstring = $faultstring;
1082
        $this->faultdetail = $faultdetail;
1083
    }
1084
1085
    /**
1086
     * Serialize a fault
1087
     *
1088
     * @return    string    The serialization of the fault instance.
1089
     */
1090
    public function serialize()
1091
    {
1092
        $ns_string = '';
1093
        foreach ($this->namespaces as $k => $v) {
1094
            $ns_string .= "\n  xmlns:$k=\"$v\"";
1095
        }
1096
        $return_msg = '<?xml version="1.0" encoding="'
1097
                      . $this->soap_defencoding
1098
                      . '"?>'
1099
                      . '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'
1100
                      . $ns_string
1101
                      . ">\n"
1102
                      . '<SOAP-ENV:Body>'
1103
                      . '<SOAP-ENV:Fault>'
1104
                      . $this->serialize_val($this->faultcode, 'faultcode')
1105
                      . $this->serialize_val($this->faultactor, 'faultactor')
1106
                      . $this->serialize_val($this->faultstring, 'faultstring')
1107
                      . $this->serialize_val($this->faultdetail, 'detail')
1108
                      . '</SOAP-ENV:Fault>'
1109
                      . '</SOAP-ENV:Body>'
1110
                      . '</SOAP-ENV:Envelope>';
1111
1112
        return $return_msg;
1113
    }
1114
}
1115
1116
/**
1117
 * Backward compatibility
1118
 */
1119
class Soap_fault extends Nusoap_fault
1120
{
1121
}
1122
1123
/**
1124
 * Parses an XML Schema, allows access to it's data, other utility methods.
1125
 * imperfect, no validation... yet, but quite functional.
1126
 *
1127
 * @author   Dietrich Ayala <[email protected]>
1128
 * @author   Scott Nichol <[email protected]>
1129
 */
1130
class Nusoap_xmlschema extends Nusoap_base
1131
{
1132
    // files
1133
    public $schema = '';
1134
    public $xml    = '';
1135
    // namespaces
1136
    public $enclosingNamespaces;
1137
    // schema info
1138
    public $schemaInfo            = [];
1139
    public $schemaTargetNamespace = '';
1140
    // types, elements, attributes defined by the schema
1141
    public $attributes       = [];
1142
    public $complexTypes     = [];
1143
    public $complexTypeStack = [];
1144
    public $currentComplexType;
1145
    public $elements         = [];
1146
    public $elementStack     = [];
1147
    public $currentElement;
1148
    public $simpleTypes      = [];
1149
    public $simpleTypeStack  = [];
1150
    public $currentSimpleType;
1151
    // imports
1152
    public $imports = [];
1153
    // parser vars
1154
    public $parser;
1155
    public $position         = 0;
1156
    public $depth            = 0;
1157
    public $depth_array      = [];
1158
    public $message          = [];
1159
    public $defaultNamespace = [];
1160
1161
    /**
1162
     * Constructor
1163
     *
1164
     * @param string       $schema     schema document URI
1165
     * @param string       $xml        xml document URI
1166
     * @param array|string $namespaces namespaces defined in enclosing XML
1167
     */
1168
    public function __construct($schema = '', $xml = '', $namespaces = [])
1169
    {
1170
        parent::__construct();
1171
        $this->debug('Nusoap_xmlschema class instantiated, inside constructor');
1172
        // files
1173
        $this->schema = $schema;
1174
        $this->xml    = $xml;
1175
        // namespaces
1176
        $this->enclosingNamespaces = $namespaces;
1177
        $this->namespaces          = array_merge($this->namespaces, $namespaces);
0 ignored issues
show
Bug introduced by
It seems like $namespaces can also be of type string; however, parameter $array2 of array_merge() does only seem to accept null|array, 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

1177
        $this->namespaces          = array_merge($this->namespaces, /** @scrutinizer ignore-type */ $namespaces);
Loading history...
1178
        // parse schema file
1179
        if ('' !== $schema) {
1180
            $this->debug('initial schema file: ' . $schema);
1181
            $this->parseFile($schema, 'schema');
1182
        }
1183
1184
        // parse xml file
1185
        if ('' !== $xml) {
1186
            $this->debug('initial xml file: ' . $xml);
1187
            $this->parseFile($xml, 'xml');
1188
        }
1189
    }
1190
1191
    /**
1192
     * Parse an XML file
1193
     *
1194
     * @param string $xml  path/URL to XML file
1195
     * @param string $type (schema | xml)
1196
     * @return bool
1197
     */
1198
    public function parseFile($xml, $type)
1199
    {
1200
        // parse xml file
1201
        if ('' !== $xml) {
1202
            $xmlStr = @file_get_contents($xml);
1203
            if ('' === $xmlStr) {
1204
                $msg = 'Error reading XML from ' . $xml;
1205
                $this->setError($msg);
1206
                $this->debug($msg);
1207
1208
                return false;
1209
            }
1210
1211
            $this->debug("parsing $xml");
1212
            $this->parseString($xmlStr, $type);
1213
            $this->debug("done parsing $xml");
1214
1215
            return true;
1216
        }
1217
1218
        return false;
1219
    }
1220
1221
    /**
1222
     * Parse an XML string
1223
     *
1224
     * @param    string $xml  path or URL
1225
     * @param    string $type (schema|xml)
1226
     */
1227
    private function parseString($xml, $type)
1228
    {
1229
        // parse xml string
1230
        if ('' !== $xml) {
1231
            // Create an XML parser.
1232
            $this->parser = xml_parser_create();
1233
            // Set the options for parsing the XML data.
1234
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1235
            // Set the object for the parser.
1236
            xml_set_object($this->parser, $this);
1237
            // Set the element handlers for the parser.
1238
            if ('schema' === $type) {
1239
                xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
1240
                xml_set_character_data_handler($this->parser, 'schemaCharacterData');
1241
            } elseif ('xml' === $type) {
1242
                xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
1243
                xml_set_character_data_handler($this->parser, 'xmlCharacterData');
1244
            }
1245
1246
            // Parse the XML file.
1247
            if (!xml_parse($this->parser, $xml, true)) {
1248
                // Display an error message.
1249
                $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)));
1250
                $this->debug($errstr);
1251
                $this->debug("XML payload:\n" . $xml);
1252
                $this->setError($errstr);
1253
            }
1254
1255
            xml_parser_free($this->parser);
1256
            unset($this->parser);
1257
        } else {
1258
            $this->debug('no xml passed to parseString()!!');
1259
            $this->setError('no xml passed to parseString()!!');
1260
        }
1261
    }
1262
1263
    /**
1264
     * Gets a type name for an unnamed type
1265
     * @param    string  $ename  Element name
1266
     * @return    string    A type name for an unnamed type
1267
     */
1268
    private function CreateTypeName($ename)
1269
    {
1270
        $scope = '';
1271
        for ($i = 0, $iMax = count($this->complexTypeStack); $i < $iMax; ++$i) {
1272
            $scope .= $this->complexTypeStack[$i] . '_';
1273
        }
1274
1275
        return $scope . $ename . '_ContainedType';
1276
    }
1277
1278
    /**
1279
     * Start-element handler
1280
     *
1281
     * @param    string       $parser XML parser object
1282
     * @param    string       $name   element name
1283
     * @param    string|array $attrs  associative array of attributes
1284
     */
1285
    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

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

1636
    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...
1637
    {
1638
        // bring depth down a notch
1639
        $this->depth--;
1640
        // position of current element is equal to the last value left in depth_array for my depth
1641
        if (isset($this->depth_array[$this->depth])) {
1642
            $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...
1643
        }
1644
        // get element prefix
1645
        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...
1646
            // get unqualified name
1647
            $name = $this->getLocalPart($name);
1648
        } else {
1649
            $prefix = '';
1650
        }
1651
        // move on...
1652
        if ('complexType' === $name) {
1653
            $this->xdebug('done processing complexType ' . ($this->currentComplexType ?: '(unknown)'));
1654
            $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1655
            $this->currentComplexType = array_pop($this->complexTypeStack);
1656
            //$this->currentElement = false;
1657
        }
1658
        if ('element' === $name) {
1659
            $this->xdebug('done processing element ' . ($this->currentElement ?: '(unknown)'));
1660
            $this->currentElement = array_pop($this->elementStack);
1661
        }
1662
        if ('simpleType' === $name) {
1663
            $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ?: '(unknown)'));
1664
            $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1665
            $this->currentSimpleType = array_pop($this->simpleTypeStack);
1666
        }
1667
    }
1668
1669
    /**
1670
     * Element content handler
1671
     *
1672
     * @param    string $parser XML parser object
1673
     * @param    string $data   element content
1674
     */
1675
    private function schemaCharacterData($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

1675
    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...
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...
1676
    {
1677
        $pos                          = $this->depth_array[$this->depth - 1];
1678
        $this->message[$pos]['cdata'] .= $data;
1679
    }
1680
1681
    /**
1682
     * Serialize the schema
1683
     */
1684
    public function serializeSchema()
1685
    {
1686
        $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1687
        $xml          = '';
1688
        // imports
1689
        if (is_array($this->imports) && count($this->imports) > 0) {
1690
            foreach ($this->imports as $ns => $list) {
1691
                foreach ($list as $ii) {
1692
                    if ('' !== $ii['location']) {
1693
                        $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" >\n";
1694
                    } else {
1695
                        $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" >\n";
1696
                    }
1697
                }
1698
            }
1699
        }
1700
        // complex types
1701
        foreach ($this->complexTypes as $typeName => $attrs) {
1702
            $contentStr = '';
1703
            // serialize child elements
1704
            if (isset($attrs['elements']) && (count($attrs['elements']) > 0)) {
1705
                foreach ($attrs['elements'] as $element => $eParts) {
1706
                    if (isset($eParts['ref'])) {
1707
                        $contentStr .= "   <$schemaPrefix:element ref=\"$element\">\n";
1708
                    } else {
1709
                        $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . '"';
1710
                        foreach ($eParts as $aName => $aValue) {
1711
                            // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1712
                            if ('name' !== $aName && 'type' !== $aName) {
1713
                                $contentStr .= " $aName=\"$aValue\"";
1714
                            }
1715
                        }
1716
                        $contentStr .= ">\n";
1717
                    }
1718
                }
1719
                // compositor wraps elements
1720
                if (isset($attrs['compositor']) && ('' !== $attrs['compositor'])) {
1721
                    $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n" . $contentStr . "  </$schemaPrefix:$attrs[compositor]>\n";
1722
                }
1723
            }
1724
            // attributes
1725
            if (isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)) {
1726
                foreach ($attrs['attrs'] as $attr => $aParts) {
1727
                    $contentStr .= "    <$schemaPrefix:attribute";
1728
                    foreach ($aParts as $a => $v) {
1729
                        if ('ref' === $a || 'type' === $a) {
1730
                            $contentStr .= " $a=\"" . $this->contractQName($v) . '"';
1731
                        } elseif ('http://schemas.xmlsoap.org/wsdl/:arrayType' === $a) {
1732
                            $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1733
                            $contentStr .= ' Wsdl:arrayType="' . $this->contractQName($v) . '"';
1734
                        } else {
1735
                            $contentStr .= " $a=\"$v\"";
1736
                        }
1737
                    }
1738
                    $contentStr .= ">\n";
1739
                }
1740
            }
1741
            // if restriction
1742
            if (isset($attrs['restrictionBase']) && '' !== $attrs['restrictionBase']) {
1743
                $contentStr = "   <$schemaPrefix:restriction base=\"" . $this->contractQName($attrs['restrictionBase']) . "\">\n" . $contentStr . "   </$schemaPrefix:restriction>\n";
1744
                // complex or simple content
1745
                if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)) {
1746
                    $contentStr = "  <$schemaPrefix:complexContent>\n" . $contentStr . "  </$schemaPrefix:complexContent>\n";
1747
                }
1748
            }
1749
            // finalize complex type
1750
            if ('' !== $contentStr) {
1751
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n" . $contentStr . " </$schemaPrefix:complexType>\n";
1752
            } else {
1753
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n";
1754
            }
1755
            $xml .= $contentStr;
1756
        }
1757
        // simple types
1758
        if (isset($this->simpleTypes) && count($this->simpleTypes) > 0) {
1759
            foreach ($this->simpleTypes as $typeName => $eParts) {
1760
                $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"" . $this->contractQName($eParts['type']) . "\">\n";
1761
                if (isset($eParts['enumeration'])) {
1762
                    foreach ($eParts['enumeration'] as $e) {
1763
                        $xml .= "  <$schemaPrefix:enumeration value=\"$e\">\n";
1764
                    }
1765
                }
1766
                $xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1767
            }
1768
        }
1769
        // elements
1770
        if (isset($this->elements) && count($this->elements) > 0) {
1771
            foreach ($this->elements as $element => $eParts) {
1772
                $xml .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\">\n";
1773
            }
1774
        }
1775
        // attributes
1776
        if (isset($this->attributes) && count($this->attributes) > 0) {
1777
            foreach ($this->attributes as $attr => $aParts) {
1778
                $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"" . $this->contractQName($aParts['type']) . "\"\n>";
1779
            }
1780
        }
1781
        // finish 'er up
1782
        $attr = '';
1783
        foreach ($this->schemaInfo as $k => $v) {
1784
            if ('elementFormDefault' === $k || 'attributeFormDefault' === $k) {
1785
                $attr .= " $k=\"$v\"";
1786
            }
1787
        }
1788
        $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1789
        foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
0 ignored issues
show
Bug introduced by
It seems like $this->enclosingNamespaces can also be of type string; however, parameter $array2 of array_diff() does only seem to accept array, 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

1789
        foreach (array_diff($this->usedNamespaces, /** @scrutinizer ignore-type */ $this->enclosingNamespaces) as $nsp => $ns) {
Loading history...
1790
            $el .= " xmlns:$nsp=\"$ns\"";
1791
        }
1792
        $xml = $el . ">\n" . $xml . "</$schemaPrefix:schema>\n";
1793
1794
        return $xml;
1795
    }
1796
1797
    /**
1798
     * Adds debug data to the clas level debug string
1799
     *
1800
     * @param    string $string debug data
1801
     */
1802
    private function xdebug($string)
1803
    {
1804
        $this->debug('<' . $this->schemaTargetNamespace . '> ' . $string);
1805
    }
1806
1807
    /**
1808
     * Get the PHP type of a user defined type in the schema
1809
     * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1810
     * Returns false if no type exists, or not w/ the given namespace
1811
     * Else returns a string that is either a native php type, or 'struct'
1812
     *
1813
     * @param string $type name of defined type
1814
     * @param string $ns   namespace of type
1815
     * @return mixed
1816
     * @deprecated
1817
     */
1818
    public function getPHPType($type, $ns)
1819
    {
1820
        if (isset($this->typemap[$ns][$type])) {
1821
            //print "found type '$type' and ns $ns in typemap<br>";
1822
            return $this->typemap[$ns][$type];
1823
        }
1824
1825
        if (isset($this->complexTypes[$type])) {
1826
            //print "getting type '$type' and ns $ns from complexTypes array<br>";
1827
            return $this->complexTypes[$type]['phpType'];
1828
        }
1829
1830
        return false;
1831
    }
1832
1833
    /**
1834
     * Returns an associative array of information about a given type
1835
     * Returns false if no type exists by the given name
1836
     *
1837
     *    For a complexType typeDef = array(
1838
     *    'restrictionBase' => '',
1839
     *    'phpType' => '',
1840
     *    'compositor' => '(sequence|all)',
1841
     *    'elements' => array(), // refs to elements array
1842
     *    'attrs' => array() // refs to attributes array
1843
     *    ... and so on (see addComplexType)
1844
     *    )
1845
     *
1846
     *   For simpleType or element, the array has different keys.
1847
     *
1848
     * @param string $type
1849
     * @return mixed
1850
     * @see    addComplexType
1851
     * @see    addSimpleType
1852
     * @see    addElement
1853
     */
1854
    public function getTypeDef($type)
1855
    {
1856
        //$this->debug("in getTypeDef for type $type");
1857
        if ('^' === mb_substr($type, -1)) {
1858
            $is_element = 1;
1859
            $type       = mb_substr($type, 0, -1);
1860
        } else {
1861
            $is_element = 0;
1862
        }
1863
1864
        if ((!$is_element) && isset($this->complexTypes[$type])) {
1865
            $this->xdebug("in getTypeDef, found complexType $type");
1866
1867
            return $this->complexTypes[$type];
1868
        }
1869
1870
        if ((!$is_element) && isset($this->simpleTypes[$type])) {
1871
            $this->xdebug("in getTypeDef, found simpleType $type");
1872
            if (!isset($this->simpleTypes[$type]['phpType'])) {
1873
                // get info for type to tack onto the simple type
1874
                // TODO: can this ever really apply (i.e. what is a simpleType really?)
1875
                $uqType = mb_substr($this->simpleTypes[$type]['type'], mb_strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1876
                $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...
1877
                $etype  = $this->getTypeDef($uqType);
1878
                if ($etype) {
1879
                    $this->xdebug("in getTypeDef, found type for simpleType $type:");
1880
                    $this->xdebug($this->varDump($etype));
1881
                    if (isset($etype['phpType'])) {
1882
                        $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1883
                    }
1884
                    if (isset($etype['elements'])) {
1885
                        $this->simpleTypes[$type]['elements'] = $etype['elements'];
1886
                    }
1887
                }
1888
            }
1889
1890
            return $this->simpleTypes[$type];
1891
        } elseif (isset($this->elements[$type])) {
1892
            $this->xdebug("in getTypeDef, found element $type");
1893
            if (!isset($this->elements[$type]['phpType'])) {
1894
                // get info for type to tack onto the element
1895
                $uqType = mb_substr($this->elements[$type]['type'], mb_strrpos($this->elements[$type]['type'], ':') + 1);
1896
                $ns     = mb_substr($this->elements[$type]['type'], 0, mb_strrpos($this->elements[$type]['type'], ':'));
1897
                $etype  = $this->getTypeDef($uqType);
1898
                if ($etype) {
1899
                    $this->xdebug("in getTypeDef, found type for element $type:");
1900
                    $this->xdebug($this->varDump($etype));
1901
                    if (isset($etype['phpType'])) {
1902
                        $this->elements[$type]['phpType'] = $etype['phpType'];
1903
                    }
1904
                    if (isset($etype['elements'])) {
1905
                        $this->elements[$type]['elements'] = $etype['elements'];
1906
                    }
1907
                    if (isset($etype['extensionBase'])) {
1908
                        $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
1909
                    }
1910
                } elseif ('http://www.w3.org/2001/XMLSchema' === $ns) {
1911
                    $this->xdebug("in getTypeDef, element $type is an XSD type");
1912
                    $this->elements[$type]['phpType'] = 'scalar';
1913
                }
1914
            }
1915
1916
            return $this->elements[$type];
1917
        } elseif (isset($this->attributes[$type])) {
1918
            $this->xdebug("in getTypeDef, found attribute $type");
1919
1920
            return $this->attributes[$type];
1921
        } elseif (preg_match('/_ContainedType$/', $type)) {
1922
            $this->xdebug("in getTypeDef, have an untyped element $type");
1923
            $typeDef['typeClass'] = 'simpleType';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$typeDef was never initialized. Although not strictly required by PHP, it is generally a good practice to add $typeDef = array(); before regardless.
Loading history...
1924
            $typeDef['phpType']   = 'scalar';
1925
            $typeDef['type']      = 'http://www.w3.org/2001/XMLSchema:string';
1926
1927
            return $typeDef;
1928
        }
1929
        $this->xdebug("in getTypeDef, did not find $type");
1930
1931
        return false;
1932
    }
1933
1934
    /**
1935
     * Returns a sample serialization of a given type, or false if no type by the given name
1936
     *
1937
     * @param string $type name of type
1938
     * @return mixed
1939
     * @deprecated
1940
     */
1941
    public function serializeTypeDef($type)
1942
    {
1943
        //print "in sTD() for type $type<br>";
1944
        if (false !== ($typeDef = $this->getTypeDef($type))) {
1945
            $str .= '<' . $type;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $str seems to be never defined.
Loading history...
1946
            if (is_array($typeDef['attrs'])) {
1947
                foreach ($typeDef['attrs'] as $attName => $data) {
1948
                    $str .= " $attName=\"{type = " . $data['type'] . '}"';
1949
                }
1950
            }
1951
            $str .= ' xmlns="' . $this->schema['targetNamespace'] . '"';
1952
            if (is_array($typeDef['elements']) && count($typeDef['elements']) > 0) {
1953
                $str .= '>';
1954
                foreach ($typeDef['elements'] as $element => $eData) {
1955
                    $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

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

2283
        curl_setopt(/** @scrutinizer ignore-type */ $this->ch, $option, $value);
Loading history...
2284
    }
2285
2286
    /**
2287
     * Sets an HTTP header
2288
     *
2289
     * @param string $name  The name of the header
2290
     * @param string $value The value of the header
2291
     */
2292
    private function setHeader($name, $value)
2293
    {
2294
        $this->outgoing_headers[$name] = $value;
2295
        $this->debug("set header $name: $value");
2296
    }
2297
2298
    /**
2299
     * Unsets an HTTP header
2300
     *
2301
     * @param string $name The name of the header
2302
     */
2303
    private function unsetHeader($name)
2304
    {
2305
        if (isset($this->outgoing_headers[$name])) {
2306
            $this->debug("unset header $name");
2307
            unset($this->outgoing_headers[$name]);
2308
        }
2309
    }
2310
2311
    /**
2312
     * Sets the URL to which to connect
2313
     *
2314
     * @param string $url The URL to which to connect
2315
     */
2316
    private function setURL($url)
2317
    {
2318
        $this->url = $url;
2319
        $u         = parse_url($url);
2320
        foreach ($u as $k => $v) {
2321
            $this->debug("parsed URL $k = $v");
2322
            $this->$k = $v;
2323
        }
2324
2325
        // add any GET params to path
2326
        if (isset($u['query']) && '' !== $u['query']) {
2327
            $this->path .= '?' . $u['query'];
2328
        }
2329
2330
        // set default port
2331
        if (!isset($u['port'])) {
2332
            $this->port = 80;
2333
            if ('https' === $u['scheme']) {
2334
                $this->port = 443;
2335
            }
2336
        }
2337
2338
        $this->uri        = $this->path;
2339
        $this->digest_uri = $this->uri;
2340
        // build headers
2341
        if (!isset($u['port'])) {
2342
            $this->setHeader('Host', $this->host);
2343
        } else {
2344
            $this->setHeader('Host', $this->host . ':' . $this->port);
2345
        }
2346
2347
        if (isset($u['user']) && '' !== $u['user']) {
2348
            $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2349
        }
2350
    }
2351
2352
    /**
2353
     * Gets the I/O method to use
2354
     *
2355
     * @return    string    I/O method to use (socket|curl|unknown)
2356
     */
2357
    private function io_method()
2358
    {
2359
        if ($this->use_curl || ('https' === $this->scheme) || ('http' === $this->scheme && 'ntlm' === $this->authtype) || ('http' === $this->scheme && is_array($this->proxy) && 'ntlm' === $this->proxy['authtype'])) {
2360
            return 'curl';
2361
        }
2362
        if (('http' === $this->scheme || 'ssl' === $this->scheme) && 'ntlm' !== $this->authtype && (!is_array($this->proxy) || 'ntlm' !== $this->proxy['authtype'])) {
2363
            return 'socket';
2364
        }
2365
2366
        return 'unknown';
2367
    }
2368
2369
    /**
2370
     * Establish an HTTP connection
2371
     *
2372
     * @param int $connection_timeout set connection timeout in seconds
2373
     * @param int $response_timeout   set response timeout in seconds
2374
     * @return bool true if connected, false if not
2375
     */
2376
    private function connect($connection_timeout = 0, $response_timeout = 30)
2377
    {
2378
        // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2379
        // "regular" socket.
2380
        // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2381
        //       loaded), and until PHP5 stream_get_wrappers is not available.
2382
        //	  	if ($this->scheme == 'https') {
2383
        //          if (version_compare(PHP_VERSION, '4.3.0') >= 0) {
2384
        //		  		if (extension_loaded('openssl')) {
2385
        //		  			$this->scheme = 'ssl';
2386
        //		  			$this->debug('Using SSL over OpenSSL');
2387
        //		  		}
2388
        //		  	}
2389
        //		}
2390
        $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2391
        if ('socket' === $this->io_method()) {
2392
            if (!is_array($this->proxy)) {
2393
                $host = $this->host;
2394
                $port = $this->port;
0 ignored issues
show
Unused Code introduced by
The assignment to $port is dead and can be removed.
Loading history...
2395
            } else {
2396
                $host = $this->proxy['host'];
2397
                $port = $this->proxy['port'];
2398
            }
2399
2400
            // use persistent connection
2401
            if ($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) {
2402
                if (!feof($this->fp)) {
2403
                    $this->debug('Re-use persistent connection');
2404
2405
                    return true;
2406
                }
2407
                fclose($this->fp);
2408
                $this->debug('Closed persistent connection at EOF');
2409
            }
2410
2411
            // munge host if using OpenSSL
2412
            if ('ssl' === $this->scheme) {
2413
                $host = 'ssl://' . $host;
2414
            }
2415
            $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2416
            // open socket
2417
            if ($connection_timeout > 0) {
2418
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str, $connection_timeout);
0 ignored issues
show
Bug introduced by
$this->port of type string is incompatible with the type integer expected by parameter $port of fsockopen(). ( Ignorable by Annotation )

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

2418
                $this->fp = @fsockopen($host, /** @scrutinizer ignore-type */ $this->port, $this->errno, $this->error_str, $connection_timeout);
Loading history...
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...
2419
            } else {
2420
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str);
2421
            }
2422
2423
            // test pointer
2424
            if (!$this->fp) {
2425
                $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2426
                if ($this->errno) {
2427
                    $msg .= ', Error (' . $this->errno . '): ' . $this->error_str;
2428
                } else {
2429
                    $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2430
                }
2431
                $this->debug($msg);
2432
                $this->setError($msg);
2433
2434
                return false;
2435
            }
2436
2437
            // set response timeout
2438
            $this->debug('set response timeout to ' . $response_timeout);
2439
            stream_set_timeout($this->fp, $response_timeout);
2440
            $this->debug('socket connected');
2441
2442
            return true;
2443
        }
2444
        if ('curl' === $this->io_method()) {
2445
            if (!extension_loaded('curl')) {
2446
                //			$this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2447
                $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.');
2448
2449
                return false;
2450
            }
2451
            // Avoid warnings when PHP does not have these options
2452
            $CURLOPT_CONNECTIONTIMEOUT = 78;
2453
            if (defined('CURLOPT_CONNECTIONTIMEOUT')) {
2454
                $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...
2455
            }
2456
            $CURLOPT_HTTPAUTH = 107;
2457
            if (defined('CURLOPT_HTTPAUTH')) {
2458
                $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2459
            }
2460
            $CURLOPT_PROXYAUTH = 111;
2461
            if (defined('CURLOPT_PROXYAUTH')) {
2462
                $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2463
            }
2464
            $CURLAUTH_BASIC = 1;
2465
            if (defined('CURLAUTH_BASIC')) {
2466
                $CURLAUTH_BASIC = CURLAUTH_BASIC;
2467
            }
2468
            $CURLAUTH_DIGEST = 2;
2469
            if (defined('CURLAUTH_DIGEST')) {
2470
                $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2471
            }
2472
            $CURLAUTH_NTLM = 8;
2473
            if (defined('CURLAUTH_NTLM')) {
2474
                $CURLAUTH_NTLM = CURLAUTH_NTLM;
2475
            }
2476
2477
            $this->debug('connect using cURL');
2478
            // init CURL
2479
            $this->ch = curl_init();
2480
            // set url
2481
            $hostURL = ('' !== $this->port) ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2482
            // add path
2483
            $hostURL .= $this->path;
2484
            $this->setCurlOption(CURLOPT_URL, $hostURL);
2485
            // follow location headers (re-directs)
2486
            $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2487
            // ask for headers in the response output
2488
            $this->setCurlOption(CURLOPT_HEADER, 1);
2489
            // ask for the response output as the return value
2490
            $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2491
            // encode
2492
            // We manage this ourselves through headers and encoding
2493
            //		if(function_exists('gzuncompress')){
2494
            //			$this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2495
            //		}
2496
            // persistent connection
2497
            if ($this->persistentConnection) {
2498
                // I believe the following comment is now bogus, having applied to
2499
                // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2500
                // The way we send data, we cannot use persistent connections, since
2501
                // there will be some "junk" at the end of our request.
2502
                //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2503
                $this->persistentConnection = false;
2504
                $this->setHeader('Connection', 'close');
2505
            }
2506
            // set timeouts
2507
            if (0 !== $connection_timeout) {
2508
                $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2509
            }
2510
            if (0 !== $response_timeout) {
2511
                $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2512
            }
2513
2514
            if ('https' === $this->scheme) {
2515
                $this->debug('set cURL SSL verify options');
2516
                // recent versions of cURL turn on peer/host checking by default,
2517
                // while PHP binaries are not compiled with a default location for the
2518
                // CA cert bundle, so disable peer/host checking.
2519
                //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2520
                $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2521
                $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2522
                // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2523
                if ('certificate' === $this->authtype) {
2524
                    $this->debug('set cURL certificate options');
2525
                    if (isset($this->certRequest['cainfofile'])) {
2526
                        $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2527
                    }
2528
                    if (isset($this->certRequest['verifypeer'])) {
2529
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2530
                    } else {
2531
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2532
                    }
2533
                    if (isset($this->certRequest['verifyhost'])) {
2534
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2535
                    } else {
2536
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2537
                    }
2538
                    if (isset($this->certRequest['sslcertfile'])) {
2539
                        $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2540
                    }
2541
                    if (isset($this->certRequest['sslkeyfile'])) {
2542
                        $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2543
                    }
2544
                    if (isset($this->certRequest['passphrase'])) {
2545
                        $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2546
                    }
2547
                    if (isset($this->certRequest['certpassword'])) {
2548
                        $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2549
                    }
2550
                }
2551
            }
2552
            if ($this->authtype && ('certificate' !== $this->authtype)) {
2553
                if ($this->username) {
2554
                    $this->debug('set cURL username/password');
2555
                    $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2556
                }
2557
                if ('basic' === $this->authtype) {
2558
                    $this->debug('set cURL for Basic authentication');
2559
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2560
                }
2561
                if ('digest' === $this->authtype) {
2562
                    $this->debug('set cURL for digest authentication');
2563
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2564
                }
2565
                if ('ntlm' === $this->authtype) {
2566
                    $this->debug('set cURL for NTLM authentication');
2567
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2568
                }
2569
            }
2570
            if (is_array($this->proxy)) {
2571
                $this->debug('set cURL proxy options');
2572
                if ('' !== $this->proxy['port']) {
2573
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'] . ':' . $this->proxy['port']);
2574
                } else {
2575
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2576
                }
2577
                if ($this->proxy['username'] || $this->proxy['password']) {
2578
                    $this->debug('set cURL proxy authentication options');
2579
                    $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'] . ':' . $this->proxy['password']);
2580
                    if ('basic' === $this->proxy['authtype']) {
2581
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2582
                    }
2583
                    if ('ntlm' === $this->proxy['authtype']) {
2584
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2585
                    }
2586
                }
2587
            }
2588
            $this->debug('cURL connection set up');
2589
2590
            return true;
2591
        }
2592
            $this->setError('Unknown scheme ' . $this->scheme);
2593
            $this->debug('Unknown scheme ' . $this->scheme);
2594
2595
            return false;
2596
        }
2597
2598
    /**
2599
     * Sends the SOAP request and gets the SOAP response via HTTP[S]
2600
     *
2601
     * @param    string $data             message data
2602
     * @param int       $timeout          set connection timeout in seconds
2603
     * @param int       $response_timeout set response timeout in seconds
2604
     * @param    array  $cookies          cookies to send
2605
     * @return    string data
2606
     */
2607
    public function send($data, $timeout = 0, $response_timeout = 30, $cookies = null)
2608
    {
2609
        $this->debug('entered send() with data of length: ' . mb_strlen($data));
2610
        $this->tryagain = true;
0 ignored issues
show
Bug Best Practice introduced by
The property tryagain does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
2611
        $tries          = 0;
2612
        while ($this->tryagain) {
2613
            $this->tryagain = false;
2614
            if (++$tries < 2) {
2615
                // make connnection
2616
                if (!$this->connect($timeout, $response_timeout)) {
2617
                    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...
2618
                }
2619
2620
                // send request
2621
                if (!$this->sendRequest($data, $cookies)) {
2622
                    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...
2623
                }
2624
2625
                // get response
2626
                $respdata = $this->getResponse();
2627
            } else {
2628
                $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2629
            }
2630
        }
2631
        $this->debug('end of send()');
2632
2633
        return $respdata;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $respdata does not seem to be defined for all execution paths leading up to this point.
Loading history...
2634
    }
2635
2636
    /**
2637
     * Sends the SOAP request and gets the SOAP response via HTTPS using CURL
2638
     *
2639
     * @param    string $data             message data
2640
     * @param int       $timeout          set connection timeout in seconds
2641
     * @param int       $response_timeout set response timeout in seconds
2642
     * @param    array  $cookies          cookies to send
2643
     * @return    string data
2644
     * @deprecated
2645
     */
2646
    public function sendHTTPS($data, $timeout = 0, $response_timeout = 30, $cookies = [])
2647
    {
2648
        return $this->send($data, $timeout, $response_timeout, $cookies);
2649
    }
2650
2651
    /**
2652
     * If authenticating, set user credentials here
2653
     *
2654
     * @param    string $username
2655
     * @param    string $password
2656
     * @param    string $authtype      (basic|digest|certificate|ntlm)
2657
     * @param    array  $digestRequest (keys must be nonce, nc, realm, qop)
2658
     * @param    array  $certRequest   (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2659
     */
2660
    public function setCredentials($username, $password, $authtype = 'basic', $digestRequest = [], $certRequest = [])
2661
    {
2662
        $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2663
        $this->appendDebug($this->varDump($digestRequest));
2664
        $this->debug('certRequest=');
2665
        $this->appendDebug($this->varDump($certRequest));
2666
        // cf. RFC 2617
2667
        if ('basic' === $authtype) {
2668
            $this->setHeader('Authorization', 'Basic ' . base64_encode(str_replace(':', '', $username) . ':' . $password));
2669
        } elseif ('digest' === $authtype) {
2670
            if (isset($digestRequest['nonce'])) {
2671
                $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2672
                // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2673
2674
                // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2675
                $A1 = $username . ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2676
2677
                // H(A1) = MD5(A1)
2678
                $HA1 = md5($A1);
2679
                // A2 = Method ":" digest-uri-value
2680
                $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

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

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

3171
            $this->incoming_payload = curl_exec(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3172
            $data                   = $this->incoming_payload;
3173
            $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

3173
            $cErr                   = curl_error(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3174
            if ('' !== $cErr) {
3175
                $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

3175
                $err = 'cURL ERROR: ' . curl_errno(/** @scrutinizer ignore-type */ $this->ch) . ': ' . $cErr . '<br>';
Loading history...
3176
                // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3177
                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

3177
                foreach (curl_getinfo(/** @scrutinizer ignore-type */ $this->ch) as $k => $v) {
Loading history...
3178
                    $err .= "$k: $v<br>";
3179
                }
3180
                $this->debug($err);
3181
                $this->setError($err);
3182
                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

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

3771
                    fpassthru(/** @scrutinizer ignore-type */ $fp);
Loading history...
3772
                }
3773
            } elseif ($this->wsdl) {
3774
                $this->debug('In service, serialize WSDL');
3775
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3776
                print $this->wsdl->serialize($this->debug_flag);
3777
                if ($this->debug_flag) {
3778
                    $this->debug('Wsdl:');
3779
                    $this->appendDebug($this->varDump($this->wsdl));
3780
                    print $this->getDebugAsXMLComment();
3781
                }
3782
            } else {
3783
                $this->debug('In service, there is no WSDL');
3784
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3785
                print 'This service does not provide WSDL';
3786
            }
3787
        } elseif ($this->wsdl) {
3788
            $this->debug('In service, return Web description');
3789
            print $this->wsdl->webDescription();
3790
        } else {
3791
            $this->debug('In service, no Web description');
3792
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3793
            print 'This service does not provide a Web description';
3794
        }
3795
    }
3796
3797
    /**
3798
     * Parses HTTP request headers.
3799
     *
3800
     * The following fields are set by this function (when successful)
3801
     *
3802
     * headers
3803
     * request
3804
     * xml_encoding
3805
     * SOAPAction
3806
     */
3807
    private function parse_http_headers()
3808
    {
3809
        global $_SERVER;
3810
        $this->request    = '';
3811
        $this->SOAPAction = '';
3812
        if (function_exists('getallheaders')) {
3813
            $this->debug('In parse_http_headers, use getallheaders');
3814
            $headers = getallheaders();
3815
            foreach ($headers as $k => $v) {
3816
                $k                 = mb_strtolower($k);
3817
                $this->headers[$k] = $v;
3818
                $this->request     .= "$k: $v\r\n";
3819
                $this->debug("$k: $v");
3820
            }
3821
            // get SOAPAction header
3822
            if (isset($this->headers['soapaction'])) {
3823
                $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
3824
            }
3825
            // get the character encoding of the incoming request
3826
            if (isset($this->headers['content-type']) && mb_strpos($this->headers['content-type'], '=')) {
3827
                $enc                = str_replace('"', '', mb_substr(mb_strstr($this->headers['content-type'], '='), 1));
3828
                $this->xml_encoding = 'US-ASCII';
3829
                if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3830
                    $this->xml_encoding = mb_strtoupper($enc);
3831
                }
3832
            } else {
3833
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3834
                $this->xml_encoding = 'ISO-8859-1';
3835
            }
3836
        } elseif (null !== $_SERVER && is_array($_SERVER)) {
3837
            $this->debug('In parse_http_headers, use _SERVER');
3838
            foreach ($_SERVER as $k => $v) {
3839
                if (0 === mb_strpos($k, 'HTTP_')) {
3840
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', mb_substr($k, 5))));
3841
                } else {
3842
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', $k)));
3843
                }
3844
                if ('soapaction' === $k) {
3845
                    // get SOAPAction header
3846
                    $k                = 'SOAPAction';
3847
                    $v                = str_replace('"', '', $v);
3848
                    $v                = str_replace('\\', '', $v);
3849
                    $this->SOAPAction = $v;
3850
                } elseif ('content-type' === $k) {
3851
                    // get the character encoding of the incoming request
3852
                    if (mb_strpos($v, '=')) {
3853
                        $enc                = mb_substr(mb_strstr($v, '='), 1);
3854
                        $enc                = str_replace('"', '', $enc);
3855
                        $enc                = str_replace('\\', '', $enc);
3856
                        $this->xml_encoding = 'US-ASCII';
3857
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3858
                            $this->xml_encoding = mb_strtoupper($enc);
3859
                        }
3860
                    } else {
3861
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3862
                        $this->xml_encoding = 'ISO-8859-1';
3863
                    }
3864
                }
3865
                $this->headers[$k] = $v;
3866
                $this->request     .= "$k: $v\r\n";
3867
                $this->debug("$k: $v");
3868
            }
3869
        } else {
3870
            $this->debug('In parse_http_headers, HTTP headers not accessible');
3871
            $this->setError('HTTP headers not accessible');
3872
        }
3873
    }
3874
3875
    /**
3876
     * Parses a request
3877
     *
3878
     * The following fields are set by this function (when successful)
3879
     *
3880
     * headers
3881
     * request
3882
     * xml_encoding
3883
     * SOAPAction
3884
     * request
3885
     * requestSOAP
3886
     * methodURI
3887
     * methodname
3888
     * methodparams
3889
     * requestHeaders
3890
     * document
3891
     *
3892
     * This sets the fault field on error
3893
     *
3894
     * @param    string $data XML string
3895
     */
3896
    private function parse_request($data = '')
3897
    {
3898
        $this->debug('entering parse_request()');
3899
        $this->parse_http_headers();
3900
        $this->debug('got character encoding: ' . $this->xml_encoding);
3901
        // uncompress if necessary
3902
        if (isset($this->headers['content-encoding']) && '' !== $this->headers['content-encoding']) {
3903
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3904
            if ('deflate' === $this->headers['content-encoding'] || 'gzip' === $this->headers['content-encoding']) {
3905
                // if decoding works, use it. else assume data wasn't gzencoded
3906
                if (function_exists('gzuncompress')) {
3907
                    if ('deflate' === $this->headers['content-encoding'] && $degzdata = @gzuncompress($data)) {
3908
                        $data = $degzdata;
3909
                    } elseif ('gzip' === $this->headers['content-encoding'] && $degzdata = gzinflate(mb_substr($data, 10))) {
3910
                        $data = $degzdata;
3911
                    } else {
3912
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3913
3914
                        return;
3915
                    }
3916
                } else {
3917
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3918
3919
                    return;
3920
                }
3921
            }
3922
        }
3923
        $this->request     .= "\r\n" . $data;
3924
        $data              = $this->parseRequest($this->headers, $data);
3925
        $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...
3926
        $this->debug('leaving parse_request');
3927
    }
3928
3929
    /**
3930
     * Invokes a PHP function for the requested SOAP method
3931
     *
3932
     * The following fields are set by this function (when successful)
3933
     *
3934
     * methodreturn
3935
     *
3936
     * Note that the PHP function that is called may also set the following
3937
     * fields to affect the response sent to the client
3938
     *
3939
     * responseHeaders
3940
     * outgoing_headers
3941
     *
3942
     * This sets the fault field on error
3943
     */
3944
    private function invoke_method()
3945
    {
3946
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3947
        //
3948
        // if you are debugging in this area of the code, your service uses a class to implement methods,
3949
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
3950
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
3951
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
3952
        // the XML request and reading the XML response.  you need to add the RequestElementName and
3953
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
3954
        // generates for the method.  these parameters are used to specify the correct XML element names
3955
        // for .NET to use, i.e. the names with the '.' in them.
3956
        //
3957
        $orig_methodname = $this->methodname;
3958
        if ($this->wsdl) {
3959
            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...
3960
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3961
                $this->appendDebug('opData=' . $this->varDump($this->opData));
3962
            } elseif (false !== ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction))) {
3963
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3964
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3965
                $this->appendDebug('opData=' . $this->varDump($this->opData));
3966
                $this->methodname = $this->opData['name'];
3967
            } else {
3968
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3969
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3970
3971
                return;
3972
            }
3973
        } else {
3974
            $this->debug('in invoke_method, no WSDL to validate method');
3975
        }
3976
3977
        // if a . is present in $this->methodname, we see if there is a class in scope,
3978
        // which could be referred to. We will also distinguish between two deliminators,
3979
        // to allow methods to be called a the class or an instance
3980
        $delim = '';
3981
        if (mb_strpos($this->methodname, '..') > 0) {
3982
            $delim = '..';
3983
        } elseif (mb_strpos($this->methodname, '.') > 0) {
3984
            $delim = '.';
3985
        }
3986
        $this->debug("in invoke_method, delim=$delim");
3987
        $class  = '';
3988
        $method = '';
3989
        if (mb_strlen($delim) > 0 && 1 === mb_substr_count($this->methodname, $delim)) {
3990
            $try_class = mb_substr($this->methodname, 0, mb_strpos($this->methodname, $delim));
3991
            if (class_exists($try_class)) {
3992
                // get the class and method name
3993
                $class  = $try_class;
3994
                $method = mb_substr($this->methodname, mb_strpos($this->methodname, $delim) + mb_strlen($delim));
3995
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
3996
            } else {
3997
                $this->debug("in invoke_method, class=$try_class not found");
3998
            }
3999
        } elseif (mb_strlen($delim) > 0 && mb_substr_count($this->methodname, $delim) > 1) {
4000
            $split  = explode($delim, $this->methodname);
4001
            $method = array_pop($split);
4002
            $class  = implode('\\', $split);
4003
        } else {
4004
            $try_class = '';
4005
            $this->debug('in invoke_method, no class to try');
4006
        }
4007
4008
        // does method exist?
4009
        if ('' === $class) {
4010
            if (!function_exists($this->methodname)) {
4011
                $this->debug("in invoke_method, function '$this->methodname' not found!");
4012
                $this->result = 'fault: method not found';
4013
                $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...
4014
4015
                return;
4016
            }
4017
        } else {
4018
            $method_to_compare = (0 === mb_strpos(PHP_VERSION, '4.')) ? mb_strtolower($method) : $method;
4019
            if (!in_array($method_to_compare, get_class_methods($class), true)) {
4020
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4021
                $this->result = 'fault: method not found';
4022
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4023
4024
                return;
4025
            }
4026
        }
4027
4028
        // evaluate message, getting back parameters
4029
        // verify that request parameters match the method's signature
4030
        if (!$this->verify_method($this->methodname, $this->methodparams)) {
4031
            // debug
4032
            $this->debug('ERROR: request not verified against method signature');
4033
            $this->result = 'fault: request failed validation against method signature';
4034
            // return fault
4035
            $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
4036
4037
            return;
4038
        }
4039
4040
        // if there are parameters to pass
4041
        $this->debug('in invoke_method, params:');
4042
        $this->appendDebug($this->varDump($this->methodparams));
4043
        $this->debug("in invoke_method, calling '$this->methodname'");
4044
        if (!function_exists('call_user_func_array')) {
4045
            if ('' === $class) {
4046
                $this->debug('in invoke_method, calling function using eval()');
4047
                $funcCall = "\$this->methodreturn = $this->methodname(";
4048
            } else {
4049
                if ('..' === $delim) {
4050
                    $this->debug('in invoke_method, calling class method using eval()');
4051
                    $funcCall = '$this->methodreturn = ' . $class . '::' . $method . '(';
4052
                } else {
4053
                    $this->debug('in invoke_method, calling instance method using eval()');
4054
                    // generate unique instance name
4055
                    $instname = '$inst_' . time();
4056
                    $funcCall = $instname . ' = new ' . $class . '(); ';
4057
                    $funcCall .= '$this->methodreturn = ' . $instname . '->' . $method . '(';
4058
                }
4059
            }
4060
            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...
4061
                foreach ($this->methodparams as $param) {
4062
                    if (is_array($param) || is_object($param)) {
4063
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4064
4065
                        return;
4066
                    }
4067
                    $funcCall .= "\"$param\",";
4068
                }
4069
                $funcCall = mb_substr($funcCall, 0, -1);
4070
            }
4071
            $funcCall .= ');';
4072
            $this->debug('in invoke_method, function call: ' . $funcCall);
4073
            @eval($funcCall);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
4074
        } else {
4075
            if ('' === $class) {
4076
                $this->debug('in invoke_method, calling function using call_user_func_array()');
4077
                $call_arg = $this->methodname;
4078
            // straight assignment changes $this->methodname to lower case after call_user_func_array()
4079
            } elseif ('..' === $delim) {
4080
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
4081
                $call_arg = [$class, $method];
4082
            } else {
4083
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4084
                $instance = new $class();
4085
                $call_arg = [&$instance, $method];
4086
            }
4087
            if (is_array($this->methodparams)) {
0 ignored issues
show
introduced by
The condition is_array($this->methodparams) is always true.
Loading history...
4088
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4089
            } else {
4090
                $this->methodreturn = call_user_func_array($call_arg, []);
4091
            }
4092
        }
4093
        $this->debug('in invoke_method, methodreturn:');
4094
        $this->appendDebug($this->varDump($this->methodreturn));
4095
        $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
4096
    }
4097
4098
    /**
4099
     * Serializes the return value from a PHP function into a full SOAP Envelope
4100
     *
4101
     * The following fields are set by this function (when successful)
4102
     *
4103
     * responseSOAP
4104
     *
4105
     * This sets the fault field on error
4106
     */
4107
    private function serialize_return()
4108
    {
4109
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4110
        // if fault
4111
        if (isset($this->methodreturn) && is_object($this->methodreturn) && (('soap_fault' === get_class($this->methodreturn)) || ('Nusoap_fault' === get_class($this->methodreturn)))) {
4112
            $this->debug('got a fault object from method');
4113
            $this->fault = $this->methodreturn;
4114
4115
            return;
4116
        }
4117
4118
        if ($this->methodreturnisliteralxml) {
4119
            $return_val = $this->methodreturn;
4120
        // returned value(s)
4121
        } else {
4122
            $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
4123
            $this->debug('serializing return value');
4124
            if ($this->wsdl) {
4125
                if (is_array($this->opData['output']['parts']) && count($this->opData['output']['parts']) > 1) {
4126
                    $this->debug('more than one output part, so use the method return unchanged');
4127
                    $opParams = $this->methodreturn;
4128
                } elseif (1 === count($this->opData['output']['parts'])) {
4129
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
4130
                    // TODO: verify that it is not already wrapped!
4131
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
4132
                    //	$this->debug('wrap in element named ' . $name);
4133
                    //}
4134
                    $opParams = [$this->methodreturn];
4135
                }
4136
                $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...
4137
                $this->appendDebug($this->wsdl->getDebug());
4138
                $this->wsdl->clearDebug();
4139
                if (false !== ($errstr = $this->wsdl->getError())) {
4140
                    $this->debug('got Wsdl error: ' . $errstr);
4141
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4142
4143
                    return;
4144
                }
4145
            } else {
4146
                if (isset($this->methodreturn)) {
4147
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
4148
                } else {
4149
                    $return_val = '';
4150
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
4151
                }
4152
            }
4153
        }
4154
        $this->debug('return value:');
4155
        $this->appendDebug($this->varDump($return_val));
4156
        $this->debug('serializing response');
4157
        if ($this->wsdl) {
4158
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4159
            if ('rpc' === $this->opData['style']) {
4160
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4161
                if ('literal' === $this->opData['output']['use']) {
4162
                    // 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
4163
                    if ($this->methodURI) {
4164
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4165
                    } else {
4166
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4167
                    }
4168
                } else {
4169
                    if ($this->methodURI) {
4170
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4171
                    } else {
4172
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4173
                    }
4174
                }
4175
            } else {
4176
                $this->debug('style is not rpc for serialization: assume document');
4177
                $payload = $return_val;
4178
            }
4179
        } else {
4180
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4181
            $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4182
        }
4183
        $this->result = 'successful';
4184
        if ($this->wsdl) {
4185
            //if($this->debug_flag){
4186
            $this->appendDebug($this->wsdl->getDebug());
4187
            //	}
4188
            $encodingStyle = '';
4189
            if (isset($this->opData['output']['encodingStyle'])) {
4190
                $encodingStyle = $this->opData['output']['encodingStyle'];
4191
            }
4192
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4193
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
4194
        } else {
4195
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
4196
        }
4197
        $this->debug('Leaving serialize_return');
4198
    }
4199
4200
    /**
4201
     * Sends an HTTP response
4202
     *
4203
     * The following fields are set by this function (when successful)
4204
     *
4205
     * outgoing_headers
4206
     * response
4207
     */
4208
    private function send_response()
4209
    {
4210
        $this->debug('Enter send_response');
4211
        if ($this->fault) {
4212
            $payload                  = $this->fault->serialize();
4213
            $this->outgoing_headers[] = 'HTTP/1.0 500 Internal Server Error';
4214
            $this->outgoing_headers[] = 'Status: 500 Internal Server Error';
4215
        } else {
4216
            $payload = $this->responseSOAP;
4217
            // Some combinations of PHP+Web server allow the Status
4218
            // to come through as a header.  Since OK is the default
4219
            // just do nothing.
4220
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4221
            // $this->outgoing_headers[] = "Status: 200 OK";
4222
        }
4223
        // add debug data if in debug mode
4224
        if (!empty($this->debug_flag)) {
4225
            $payload .= $this->getDebugAsXMLComment();
4226
        }
4227
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4228
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4229
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . $rev[1] . ')';
4230
        // Let the Web server decide about this
4231
        //$this->outgoing_headers[] = "Connection: Close\r\n";
4232
        $payload                  = $this->getHTTPBody($payload);
4233
        $type                     = $this->getHTTPContentType();
4234
        $charset                  = $this->getHTTPContentTypeCharset();
4235
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4236
        //begin code to compress payload - by John
4237
        // NOTE: there is no way to know whether the Web server will also compress
4238
        // this data.
4239
        if (mb_strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4240
            if (false !== mb_strpos($this->headers['accept-encoding'], 'gzip')) {
4241
                if (function_exists('gzencode')) {
4242
                    if (!empty($this->debug_flag)) {
4243
                        $payload .= '<!-- Content being gzipped -->';
4244
                    }
4245
                    $this->outgoing_headers[] = 'Content-Encoding: gzip';
4246
                    $payload                  = gzencode($payload);
4247
                } else {
4248
                    if (!empty($this->debug_flag)) {
4249
                        $payload .= '<!-- Content will not be gzipped: no gzencode -->';
4250
                    }
4251
                }
4252
            } elseif (false !== mb_strpos($this->headers['accept-encoding'], 'deflate')) {
4253
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4254
                // instead of gzcompress output,
4255
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4256
                if (function_exists('gzdeflate')) {
4257
                    if (!empty($this->debug_flag)) {
4258
                        $payload .= '<!-- Content being deflated -->';
4259
                    }
4260
                    $this->outgoing_headers[] = 'Content-Encoding: deflate';
4261
                    $payload                  = gzdeflate($payload);
4262
                } else {
4263
                    if (!empty($this->debug_flag)) {
4264
                        $payload .= '<!-- Content will not be deflated: no gzcompress -->';
4265
                    }
4266
                }
4267
            }
4268
        }
4269
        //end code
4270
        $this->outgoing_headers[] = 'Content-Length: ' . mb_strlen($payload);
4271
        reset($this->outgoing_headers);
4272
        foreach ($this->outgoing_headers as $hdr) {
4273
            header($hdr, false);
4274
        }
4275
        print $payload;
4276
        $this->response = implode("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
4277
    }
4278
4279
    /**
4280
     * Takes the value that was created by parsing the request
4281
     * and compares to the method's signature, if available.
4282
     *
4283
     * @param    string $operation The operation to be invoked
4284
     * @param    array  $request   The array of parameter values
4285
     * @return bool    Whether the operation was found
4286
     */
4287
    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

4287
    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...
4288
    {
4289
        if (isset($this->wsdl) && is_object($this->wsdl)) {
4290
            if ($this->wsdl->getOperationData($operation)) {
4291
                return true;
4292
            }
4293
        } elseif (isset($this->operations[$operation])) {
4294
            return true;
4295
        }
4296
4297
        return false;
4298
    }
4299
4300
    /**
4301
     * Processes SOAP message received from client
4302
     *
4303
     * @param    array  $headers The HTTP headers
4304
     * @param    string $data    unprocessed request data from client
4305
     * @return    mixed    value of the message, decoded into a PHP type
4306
     */
4307
    private function parseRequest($headers, $data)
4308
    {
4309
        $this->debug('Entering parseRequest() for data of length ' . mb_strlen($data) . ' headers:');
4310
        $this->appendDebug($this->varDump($headers));
4311
        if (!isset($headers['content-type'])) {
4312
            $this->setError('Request not of type text/xml (no content-type header)');
4313
4314
            return false;
4315
        }
4316
        if (false === mb_strpos($headers['content-type'], 'text/xml')) {
4317
            $this->setError('Request not of type text/xml');
4318
4319
            return false;
4320
        }
4321
        if (mb_strpos($headers['content-type'], '=')) {
4322
            $enc = str_replace('"', '', mb_substr(mb_strstr($headers['content-type'], '='), 1));
4323
            $this->debug('Got response encoding: ' . $enc);
4324
            $this->xml_encoding = 'US-ASCII';
4325
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
4326
                $this->xml_encoding = mb_strtoupper($enc);
4327
            }
4328
        } else {
4329
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4330
            $this->xml_encoding = 'ISO-8859-1';
4331
        }
4332
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4333
        // parse response, get soap parser obj
4334
        $parser = new Nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
4335
        // parser debug
4336
        $this->debug("parser debug: \n" . $parser->getDebug());
4337
        // if fault occurred during message parsing
4338
        if (false !== ($err = $parser->getError())) {
4339
            $this->result = 'fault: error in msg parsing: ' . $err;
4340
            $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
4341
        // else successfully parsed request into Soapval object
4342
        } else {
4343
            // get/set methodname
4344
            $this->methodURI  = $parser->root_struct_namespace;
4345
            $this->methodname = $parser->root_struct_name;
4346
            $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4347
            $this->debug('calling parser->get_soapbody()');
4348
            $this->methodparams = $parser->get_soapbody();
4349
            // get SOAP headers
4350
            $this->requestHeaders = $parser->getHeaders();
4351
            // get SOAP Header
4352
            $this->requestHeader = $parser->get_soapheader();
4353
            // add document for doclit support
4354
            $this->document = $parser->document;
4355
        }
4356
    }
4357
4358
    /**
4359
     * Gets the HTTP body for the current response.
4360
     *
4361
     * @param string $soapmsg The SOAP payload
4362
     * @return string The HTTP body, which includes the SOAP payload
4363
     */
4364
    private function getHTTPBody($soapmsg)
4365
    {
4366
        return $soapmsg;
4367
    }
4368
4369
    /**
4370
     * Gets the HTTP content type for the current response.
4371
     *
4372
     * Note: getHTTPBody must be called before this.
4373
     *
4374
     * @return string the HTTP content type for the current response.
4375
     */
4376
    private function getHTTPContentType()
4377
    {
4378
        return 'text/xml';
4379
    }
4380
4381
    /**
4382
     * Gets the HTTP content type charset for the current response.
4383
     * Returns false for non-text content types.
4384
     *
4385
     * Note: getHTTPBody must be called before this.
4386
     *
4387
     * @return string the HTTP content type charset for the current response.
4388
     */
4389
    private function getHTTPContentTypeCharset()
4390
    {
4391
        return $this->soap_defencoding;
4392
    }
4393
4394
    /**
4395
     * Add a method to the dispatch map (this has been replaced by the register method)
4396
     *
4397
     * @param    string $methodname
4398
     * @param    string $in  array of input values
4399
     * @param    string $out array of output values
4400
     * @deprecated
4401
     */
4402
    public function add_to_map($methodname, $in, $out)
4403
    {
4404
        $this->operations[$methodname] = ['name' => $methodname, 'in' => $in, 'out' => $out];
4405
    }
4406
4407
    /**
4408
     * Register a service function with the server
4409
     *
4410
     * @param    string $name          the name of the PHP function, class.method or class..method
4411
     * @param    array  $in            assoc array of input values: key = param name, value = param type
4412
     * @param    array  $out           assoc array of output values: key = param name, value = param type
4413
     * @param    mixed  $namespace     the element namespace for the method or false
4414
     * @param    mixed  $soapaction    the soapaction for the method or false
4415
     * @param    mixed  $style         optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4416
     * @param    mixed  $use           optional (encoded|literal) or false
4417
     * @param    string $documentation optional Description to include in WSDL
4418
     * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4419
     * @return bool
4420
     */
4421
    public function register($name, $in = [], $out = [], $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '')
4422
    {
4423
        global $_SERVER;
4424
        if ($this->externalWSDLURL) {
4425
            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...
4426
        }
4427
        if (!$name) {
4428
            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...
4429
        }
4430
        if (!is_array($in)) {
0 ignored issues
show
introduced by
The condition is_array($in) is always true.
Loading history...
4431
            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...
4432
        }
4433
        if (!is_array($out)) {
0 ignored issues
show
introduced by
The condition is_array($out) is always true.
Loading history...
4434
            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...
4435
        }
4436
        if (false === $namespace) {
4437
        }
4438
        if (false === $soapaction) {
4439
            if (null !== $_SERVER) {
4440
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
4441
                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4442
                $HTTPS = isset($_SERVER['HTTPS']) ? : 'off';
4443
            } else {
4444
                $this->setError(' _SERVER  is not available');
4445
            }
4446
            $SCHEME = 'http';
4447
            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...
4448
                $SCHEME = 'https';
4449
            }
4450
            $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...
4451
        }
4452
        if (false === $style) {
4453
            $style = 'rpc';
4454
        }
4455
        if (false === $use) {
4456
            $use = 'encoded';
4457
        }
4458
        if ('encoded' === $use && '' === $encodingStyle) {
4459
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4460
        }
4461
4462
        $this->operations[$name] = [
4463
            'name'       => $name,
4464
            'in'         => $in,
4465
            'out'        => $out,
4466
            'namespace'  => $namespace,
4467
            'soapaction' => $soapaction,
4468
            'style'      => $style,
4469
        ];
4470
        if ($this->wsdl) {
4471
            $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
4472
        }
4473
4474
        return true;
4475
    }
4476
4477
    /**
4478
     * Specify a fault to be returned to the client.
4479
     * This also acts as a flag to the server that a fault has occured.
4480
     *
4481
     * @param    string $faultcode
4482
     * @param    string $faultstring
4483
     * @param    string $faultactor
4484
     * @param    string $faultdetail
4485
     */
4486
    public function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
4487
    {
4488
        if ('' === $faultdetail && $this->debug_flag) {
4489
            $faultdetail = $this->getDebug();
4490
        }
4491
        $this->fault                   = new Nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
4492
        $this->fault->soap_defencoding = $this->soap_defencoding;
4493
    }
4494
4495
    /**
4496
     * Sets up Wsdl object.
4497
     * Acts as a flag to enable internal WSDL generation
4498
     *
4499
     * @param string $serviceName           , name of the service
4500
     * @param mixed  $namespace             optional 'tns' service namespace or false
4501
     * @param mixed  $endpoint              optional URL of service endpoint or false
4502
     * @param string $style                 optional (rpc|document) WSDL style (also specified by operation)
4503
     * @param string $transport             optional SOAP transport
4504
     * @param mixed  $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4505
     */
4506
    public function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4507
    {
4508
        global $_SERVER;
4509
4510
        if (null !== $_SERVER) {
4511
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
4512
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
4513
            $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4514
            $HTTPS = isset($_SERVER['HTTPS']) ?: 'off';
4515
        } else {
4516
            $this->setError(' $_SERVER is not available');
4517
        }
4518
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4519
        $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...
4520
        if ($colon) {
4521
            $SERVER_NAME = mb_substr($SERVER_NAME, 0, $colon);
4522
        }
4523
        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...
4524
            $SERVER_PORT = '';
4525
        } else {
4526
            $SERVER_PORT = ':' . $SERVER_PORT;
4527
        }
4528
        if (false === $namespace) {
4529
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4530
        }
4531
4532
        if (false === $endpoint) {
4533
            $SCHEME = 'http';
4534
            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...
4535
                $SCHEME = 'https';
4536
            }
4537
            $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...
4538
        }
4539
4540
        if (false === $schemaTargetNamespace) {
4541
            $schemaTargetNamespace = $namespace;
4542
        }
4543
4544
        $this->wsdl = new Wsdl();
4545
        $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...
4546
        $this->wsdl->endpoint           = $endpoint;
4547
        $this->wsdl->namespaces['tns']  = $namespace;
4548
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4549
        $this->wsdl->namespaces['Wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4550
        if ($schemaTargetNamespace !== $namespace) {
4551
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4552
        }
4553
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new Nusoap_xmlschema('', '', $this->wsdl->namespaces);
4554
        if ('document' === $style) {
4555
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4556
        }
4557
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace                                   = $schemaTargetNamespace;
4558
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = ['location' => '', 'loaded' => true];
4559
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0]          = ['location' => '', 'loaded' => true];
4560
        $this->wsdl->bindings[$serviceName . 'Binding']                                                          = [
4561
            'name'      => $serviceName . 'Binding',
4562
            'style'     => $style,
4563
            'transport' => $transport,
4564
            'portType'  => $serviceName . 'PortType',
4565
        ];
4566
        $this->wsdl->ports[$serviceName . 'Port']                                                                = [
4567
            'binding'     => $serviceName . 'Binding',
4568
            'location'    => $endpoint,
4569
            'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/',
4570
        ];
4571
    }
4572
}
4573
4574
/**
4575
 * Backward compatibility
4576
 */
4577
class Soap_server extends Nusoap_server
4578
{
4579
}
4580
4581
/**
4582
 * Parses a WSDL file, allows access to it's data, other utility methods.
4583
 * also builds WSDL structures programmatically.
4584
 *
4585
 * @author   Dietrich Ayala <[email protected]>
4586
 * @author   Scott Nichol <[email protected]>
4587
 */
4588
class Wsdl extends Nusoap_base
4589
{
4590
    // URL or filename of the root of this WSDL
4591
    public $wsdl;
4592
    // define internal arrays of bindings, ports, operations, messages, etc.
4593
    public $schemas       = [];
4594
    public $currentSchema;
4595
    public $message       = [];
4596
    public $complexTypes  = [];
4597
    public $messages      = [];
4598
    public $currentMessage;
4599
    public $currentOperation;
4600
    public $portTypes     = [];
4601
    public $currentPortType;
4602
    public $bindings      = [];
4603
    public $currentBinding;
4604
    public $ports         = [];
4605
    public $currentPort;
4606
    public $opData        = [];
4607
    public $status        = '';
4608
    public $documentation = false;
4609
    public $endpoint      = '';
4610
    // array of Wsdl docs to import
4611
    public $import = [];
4612
    // parser vars
4613
    public $parser;
4614
    public $position    = 0;
4615
    public $depth       = 0;
4616
    public $depth_array = [];
4617
    // for getting Wsdl
4618
    public $proxyhost        = '';
4619
    public $proxyport        = '';
4620
    public $proxyusername    = '';
4621
    public $proxypassword    = '';
4622
    public $timeout          = 0;
4623
    public $response_timeout = 30;
4624
    public $curl_options     = [];    // User-specified cURL options
4625
    public $use_curl         = false;            // whether to always try to use cURL
4626
    // for HTTP authentication
4627
    public $username    = '';                // Username for HTTP authentication
4628
    public $password    = '';                // Password for HTTP authentication
4629
    public $authtype    = '';                // Type of HTTP authentication
4630
    public $certRequest = [];        // Certificate for HTTP SSL authentication
4631
4632
    /**
4633
     * Constructor
4634
     *
4635
     * @param string      $wsdl             WSDL document URL
4636
     * @param bool|string $proxyhost
4637
     * @param bool|string $proxyport
4638
     * @param bool|string $proxyusername
4639
     * @param bool|string $proxypassword
4640
     * @param int         $timeout          set the connection timeout
4641
     * @param int         $response_timeout set the response timeout
4642
     * @param array       $curl_options     user-specified cURL options
4643
     * @param bool        $use_curl         try to use cURL
4644
     */
4645
    public function __construct($wsdl = '', $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $curl_options = null, $use_curl = false)
4646
    {
4647
        parent::__construct();
4648
        $this->debug("ctor Wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4649
        $this->proxyhost        = $proxyhost;
4650
        $this->proxyport        = $proxyport;
4651
        $this->proxyusername    = $proxyusername;
4652
        $this->proxypassword    = $proxypassword;
4653
        $this->timeout          = $timeout;
4654
        $this->response_timeout = $response_timeout;
4655
        if (is_array($curl_options)) {
4656
            $this->curl_options = $curl_options;
4657
        }
4658
        $this->use_curl = $use_curl;
4659
        $this->fetchWSDL($wsdl);
4660
    }
4661
4662
    /**
4663
     * Fetches the WSDL document and parses it
4664
     *
4665
     * @param $wsdl
4666
     */
4667
    public function fetchWSDL($wsdl)
4668
    {
4669
        $this->debug("parse and process WSDL path=$wsdl");
4670
        $this->wsdl = $wsdl;
4671
        // parse Wsdl file
4672
        if ('' !== $this->wsdl) {
4673
            $this->parseWSDL($this->wsdl);
4674
        }
4675
        // imports
4676
        // TODO: handle imports more properly, grabbing them in-line and nesting them
4677
        $imported_urls = [];
4678
        $imported      = 1;
4679
        while ($imported > 0) {
4680
            $imported = 0;
4681
            // Schema imports
4682
            foreach ($this->schemas as $ns => $list) {
4683
                foreach ($list as $xs) {
4684
                    $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4685
                    foreach ($xs->imports as $ns2 => $list2) {
4686
                        for ($ii = 0, $iiMax = count($list2); $ii < $iiMax; ++$ii) {
4687
                            if (!$list2[$ii]['loaded']) {
4688
                                $this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;
4689
                                $url                                                    = $list2[$ii]['location'];
4690
                                if ('' !== $url) {
4691
                                    $urlparts = parse_url($url);
4692
                                    if (!isset($urlparts['host'])) {
4693
                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . mb_substr($wsdlparts['path'], 0, mb_strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4694
                                    }
4695
                                    if (!in_array($url, $imported_urls, true)) {
4696
                                        $this->parseWSDL($url);
4697
                                        ++$imported;
4698
                                        $imported_urls[] = $url;
4699
                                    }
4700
                                } else {
4701
                                    $this->debug('Unexpected scenario: empty URL for unloaded import');
4702
                                }
4703
                            }
4704
                        }
4705
                    }
4706
                }
4707
            }
4708
            // WSDL imports
4709
            $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4710
            foreach ($this->import as $ns => $list) {
4711
                for ($ii = 0, $iiMax = count($list); $ii < $iiMax; ++$ii) {
4712
                    if (!$list[$ii]['loaded']) {
4713
                        $this->import[$ns][$ii]['loaded'] = true;
4714
                        $url                              = $list[$ii]['location'];
4715
                        if ('' !== $url) {
4716
                            $urlparts = parse_url($url);
4717
                            if (!isset($urlparts['host'])) {
4718
                                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . mb_substr($wsdlparts['path'], 0, mb_strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4719
                            }
4720
                            if (!in_array($url, $imported_urls, true)) {
4721
                                $this->parseWSDL($url);
4722
                                ++$imported;
4723
                                $imported_urls[] = $url;
4724
                            }
4725
                        } else {
4726
                            $this->debug('Unexpected scenario: empty URL for unloaded import');
4727
                        }
4728
                    }
4729
                }
4730
            }
4731
        }
4732
        // add new data to operation data
4733
        foreach ($this->bindings as $binding => $bindingData) {
4734
            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4735
                foreach ($bindingData['operations'] as $operation => $data) {
4736
                    $this->debug('post-parse data gathering for ' . $operation);
4737
                    $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'];
4738
                    $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'];
4739
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
4740
                        $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
4741
                    }
4742
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
4743
                        $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
4744
                    }
4745
                    // Set operation style if necessary, but do not override one already provided
4746
                    if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4747
                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4748
                    }
4749
                    $this->bindings[$binding]['operations'][$operation]['transport']     = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4750
                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
4751
                    $this->bindings[$binding]['operations'][$operation]['endpoint']      = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4752
                }
4753
            }
4754
        }
4755
    }
4756
4757
    /**
4758
     * Parses the Wsdl document
4759
     *
4760
     * @param string $wsdl path or URL
4761
     * @return bool
4762
     */
4763
    private function parseWSDL($wsdl = '')
4764
    {
4765
        $this->debug("parse WSDL at path=$wsdl");
4766
        if ('' === $wsdl) {
4767
            $this->debug('no Wsdl passed to parseWSDL()!!');
4768
            $this->setError('no Wsdl passed to parseWSDL()!!');
4769
4770
            return false;
4771
        }
4772
4773
        // parse $wsdl for url format
4774
        $wsdl_props = parse_url($wsdl);
4775
        if (isset($wsdl_props['scheme']) && ('http' === $wsdl_props['scheme'] || 'https' === $wsdl_props['scheme'])) {
4776
            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4777
            // get Wsdl
4778
            $tr                 = new Soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4779
            $tr->request_method = 'GET';
4780
            $tr->useSOAPAction  = false;
4781
            if ($this->proxyhost && $this->proxyport) {
4782
                $tr->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
4783
            }
4784
            if ('' !== $this->authtype) {
4785
                $tr->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
4786
            }
4787
            $tr->setEncoding('gzip, deflate');
4788
            $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4789
            //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4790
            //$this->debug("WSDL response\n" . $tr->incoming_payload);
4791
            $this->appendDebug($tr->getDebug());
4792
            // catch errors
4793
            if (false !== ($err = $tr->getError())) {
4794
                $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: ' . $err;
4795
                $this->debug($errstr);
4796
                $this->setError($errstr);
4797
                unset($tr);
4798
4799
                return false;
4800
            }
4801
            unset($tr);
4802
            $this->debug('got WSDL URL');
4803
        } else {
4804
            // $wsdl is not http(s), so treat it as a file URL or plain file path
4805
            $path = $wsdl;
4806
            if (isset($wsdl_props['scheme']) && ('file' === $wsdl_props['scheme']) && isset($wsdl_props['path'])) {
4807
                $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4808
            }
4809
            $this->debug('getting WSDL file ' . $path);
4810
            if (false !== ($fp = @fopen($path, 'rb'))) {
4811
                $wsdl_string = '';
4812
                while (false !== ($data = fread($fp, 32768))) {
4813
                    $wsdl_string .= $data;
4814
                }
4815
                fclose($fp);
4816
            } else {
4817
                $errstr = "Bad path to WSDL file $path";
4818
                $this->debug($errstr);
4819
                $this->setError($errstr);
4820
4821
                return false;
4822
            }
4823
        }
4824
        $this->debug('Parse WSDL');
4825
        // end new code added
4826
        // Create an XML parser.
4827
        $this->parser = xml_parser_create();
4828
        // Set the options for parsing the XML data.
4829
        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4830
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4831
        // Set the object for the parser.
4832
        xml_set_object($this->parser, $this);
4833
        // Set the element handlers for the parser.
4834
        xml_set_element_handler($this->parser, 'start_element', 'end_element');
4835
        xml_set_character_data_handler($this->parser, 'character_data');
4836
        // Parse the XML file.
4837
        if (!xml_parse($this->parser, $wsdl_string, true)) {
4838
            // Display an error message.
4839
            $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)));
4840
            $this->debug($errstr);
4841
            $this->debug("XML payload:\n" . $wsdl_string);
4842
            $this->setError($errstr);
4843
            xml_parser_free($this->parser);
4844
            unset($this->parser);
4845
4846
            return false;
4847
        }
4848
        // free the parser
4849
        xml_parser_free($this->parser);
4850
        unset($this->parser);
4851
        $this->debug('Parsing WSDL done');
4852
        // catch Wsdl parse errors
4853
        if ($this->getError()) {
4854
            return false;
4855
        }
4856
4857
        return true;
4858
    }
4859
4860
    /**
4861
     * Start-element handler
4862
     *
4863
     * @param string       $parser XML parser object
4864
     * @param string       $name   element name
4865
     * @param string|array $attrs  associative array of attributes
4866
     */
4867
    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...
4868
    {
4869
        if ('schema' === $this->status) {
4870
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4871
            $this->appendDebug($this->currentSchema->getDebug());
4872
            $this->currentSchema->clearDebug();
4873
        } elseif (preg_match('/schema$/', $name)) {
4874
            $this->debug('Parsing WSDL schema');
4875
            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4876
            $this->status        = 'schema';
4877
            $this->currentSchema = new Nusoap_xmlschema('', '', $this->namespaces);
4878
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4879
            $this->appendDebug($this->currentSchema->getDebug());
4880
            $this->currentSchema->clearDebug();
4881
        } else {
4882
            // position in the total number of elements, starting from 0
4883
            $pos   = $this->position++;
4884
            $depth = $this->depth++;
4885
            // set self as current value for this depth
4886
            $this->depth_array[$depth] = $pos;
4887
            $this->message[$pos]       = ['cdata' => ''];
4888
            // process attributes
4889
            if (is_array($attrs) && count($attrs) > 0) {
4890
                // register namespace declarations
4891
                foreach ($attrs as $k => $v) {
4892
                    if (preg_match('/^xmlns/', $k)) {
4893
                        if (false !== ($ns_prefix = mb_substr(mb_strrchr($k, ':'), 1))) {
4894
                            $this->namespaces[$ns_prefix] = $v;
4895
                        } else {
4896
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4897
                        }
4898
                        if ('http://www.w3.org/2001/XMLSchema' === $v || 'http://www.w3.org/1999/XMLSchema' === $v || 'http://www.w3.org/2000/10/XMLSchema' === $v) {
4899
                            $this->XMLSchemaVersion  = $v;
4900
                            $this->namespaces['xsi'] = $v . '-instance';
4901
                        }
4902
                    }
4903
                }
4904
                // expand each attribute prefix to its namespace
4905
                foreach ($attrs as $k => $v) {
4906
                    $k = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
4907
                    if ('location' !== $k && 'soapAction' !== $k && 'namespace' !== $k) {
4908
                        $v = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
4909
                    }
4910
                    $eAttrs[$k] = $v;
4911
                }
4912
                $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 4905. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
4913
            } else {
4914
                $attrs = [];
4915
            }
4916
            // Set default prefix and namespace
4917
            // to prevent error Undefined variable $prefix and $namespace if (preg_match('/:/', $name)) return 0 or FALSE
4918
            $prefix    = '';
4919
            $namespace = '';
4920
            // get element prefix, namespace and name
4921
            if (preg_match('/:/', $name)) {
4922
                // get ns prefix
4923
                $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
4924
                // get ns
4925
                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4926
                // get unqualified name
4927
                $name = mb_substr(mb_strstr($name, ':'), 1);
4928
            }
4929
            // process attributes, expanding any prefixes to namespaces
4930
            // find status, register data
4931
            switch ($this->status) {
4932
                case 'message':
4933
                    if ('part' === $name) {
4934
                        if (isset($attrs['type'])) {
4935
                            $this->debug('msg ' . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
4936
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4937
                        }
4938
                        if (isset($attrs['element'])) {
4939
                            $this->debug('msg ' . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
4940
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
4941
                        }
4942
                    }
4943
4944
                    break;
4945
                case 'portType':
4946
                    switch ($name) {
4947
                        case 'operation':
4948
                            $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...
4949
                            $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4950
                            if (isset($attrs['parameterOrder'])) {
4951
                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4952
                            }
4953
4954
                            break;
4955
                        case 'documentation':
4956
                            $this->documentation = true;
4957
4958
                            break;
4959
                        // merge input/output data
4960
                        default:
4961
                            $m                                                                                      = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4962
                            $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4963
4964
                            break;
4965
                    }
4966
4967
                    break;
4968
                case 'binding':
4969
                    switch ($name) {
4970
                        case 'binding':
4971
                            // get ns prefix
4972
4973
                            if (isset($attrs['style'])) {
4974
                                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4975
                            }
4976
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4977
4978
                            break;
4979
                        case 'header':
4980
                            $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4981
4982
                            break;
4983
                        case 'operation':
4984
                            if (isset($attrs['soapAction'])) {
4985
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4986
                            }
4987
                            if (isset($attrs['style'])) {
4988
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4989
                            }
4990
                            if (isset($attrs['name'])) {
4991
                                $this->currentOperation = $attrs['name'];
4992
                                $this->debug("current binding operation: $this->currentOperation");
4993
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name']     = $attrs['name'];
4994
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding']  = $this->currentBinding;
4995
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4996
                            }
4997
4998
                            break;
4999
                        case 'input':
5000
                            $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...
5001
5002
                            break;
5003
                        case 'output':
5004
                            $this->opStatus = 'output';
5005
5006
                            break;
5007
                        case 'body':
5008
                            if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5009
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5010
                            } else {
5011
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5012
                            }
5013
5014
                            break;
5015
                    }
5016
5017
                    break;
5018
                case 'service':
5019
                    switch ($name) {
5020
                        case 'port':
5021
                            $this->currentPort = $attrs['name'];
5022
                            $this->debug('current port: ' . $this->currentPort);
5023
                            $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5024
5025
                            break;
5026
                        case 'address':
5027
                            $this->ports[$this->currentPort]['location']                                = $attrs['location'];
5028
                            $this->ports[$this->currentPort]['bindingType']                             = $namespace;
5029
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
5030
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint']    = $attrs['location'];
5031
5032
                            break;
5033
                    }
5034
5035
                    break;
5036
            }
5037
            // set status
5038
            switch ($name) {
5039
                case 'import':
5040
                    if (isset($attrs['location'])) {
5041
                        $this->import[$attrs['namespace']][] = ['location' => $attrs['location'], 'loaded' => false];
5042
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
5043
                    } else {
5044
                        $this->import[$attrs['namespace']][] = ['location' => '', 'loaded' => true];
5045
                        if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
5046
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
5047
                        }
5048
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
5049
                    }
5050
5051
                    break;
5052
                //wait for schema
5053
                //case 'types':
5054
                //	$this->status = 'schema';
5055
                //	break;
5056
                case 'message':
5057
                    $this->status                   = 'message';
5058
                    $this->messages[$attrs['name']] = [];
5059
                    $this->currentMessage           = $attrs['name'];
5060
5061
                    break;
5062
                case 'portType':
5063
                    $this->status                    = 'portType';
5064
                    $this->portTypes[$attrs['name']] = [];
5065
                    $this->currentPortType           = $attrs['name'];
5066
5067
                    break;
5068
                case 'binding':
5069
                    if (isset($attrs['name'])) {
5070
                        // get binding name
5071
                        if (mb_strpos($attrs['name'], ':')) {
5072
                            $this->currentBinding = $this->getLocalPart($attrs['name']);
5073
                        } else {
5074
                            $this->currentBinding = $attrs['name'];
5075
                        }
5076
                        $this->status                                      = 'binding';
5077
                        $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5078
                        $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5079
                    }
5080
5081
                    break;
5082
                case 'service':
5083
                    $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...
5084
                    $this->status      = 'service';
5085
                    $this->debug('current service: ' . $this->serviceName);
5086
5087
                    break;
5088
                case 'definitions':
5089
                    foreach ($attrs as $name => $value) {
0 ignored issues
show
introduced by
$name is overwriting one of the parameters of this function.
Loading history...
5090
                        $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...
5091
                    }
5092
5093
                    break;
5094
            }
5095
        }
5096
    }
5097
5098
    /**
5099
     * End-element handler
5100
     *
5101
     * @param string $parser XML parser object
5102
     * @param string $name   element name
5103
     */
5104
    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...
5105
    {
5106
        // unset schema status
5107
        if (/*preg_match('/types$/', $name) ||*/
5108
        preg_match('/schema$/', $name)) {
5109
            $this->status = '';
5110
            $this->appendDebug($this->currentSchema->getDebug());
5111
            $this->currentSchema->clearDebug();
5112
            $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5113
            $this->debug('Parsing WSDL schema done');
5114
        }
5115
        if ('schema' === $this->status) {
5116
            $this->currentSchema->schemaEndElement($parser, $name);
5117
        } else {
5118
            // bring depth down a notch
5119
            $this->depth--;
5120
        }
5121
        // end documentation
5122
        if ($this->documentation) {
5123
            //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5124
            //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5125
            $this->documentation = false;
5126
        }
5127
    }
5128
5129
    /**
5130
     * Element content handler
5131
     *
5132
     * @param string $parser XML parser object
5133
     * @param string $data   element content
5134
     */
5135
    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

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

5535
                            $typePrefix = $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($partType));
Loading history...
5536
                        } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5537
                            // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5538
                            $typePrefix = 'xsd';
5539
                        } else {
5540
                            foreach ($this->typemap as $ns => $types) {
5541
                                if (isset($types[$partType])) {
5542
                                    $typePrefix = $this->getPrefixFromNamespace($ns);
5543
                                }
5544
                            }
5545
                            if (!isset($typePrefix)) {
5546
                                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...
5547
                            }
5548
                        }
5549
                        $ns        = $this->getNamespaceFromPrefix($typePrefix);
5550
                        $localPart = $this->getLocalPart($partType);
5551
                        $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

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

5897
            $encodingStyle = ' SOAP-ENV:encodingStyle="' . /** @scrutinizer ignore-type */ $encodingStyle . '"';
Loading history...
5898
        }
5899
5900
        // if a Soapval has been supplied, let its type override the WSDL
5901
        if (is_object($value) && 'soapval' === get_class($value)) {
5902
            if ($value->type_ns) {
5903
                $type      = $value->type_ns . ':' . $value->type;
5904
                $forceType = true;
5905
                $this->debug("in serializeType: Soapval overrides type to $type");
5906
            } elseif ($value->type) {
5907
                $type      = $value->type;
5908
                $forceType = true;
5909
                $this->debug("in serializeType: Soapval overrides type to $type");
5910
            } else {
5911
                $forceType = false;
5912
                $this->debug('in serializeType: Soapval does not override type');
5913
            }
5914
            $attrs = $value->attributes;
5915
            $value = $value->value;
5916
            $this->debug("in serializeType: Soapval overrides value to $value");
5917
            if ($attrs) {
5918
                if (!is_array($value)) {
5919
                    $value['!'] = $value;
5920
                }
5921
                foreach ($attrs as $n => $v) {
5922
                    $value['!' . $n] = $v;
5923
                }
5924
                $this->debug('in serializeType: Soapval provides attributes');
5925
            }
5926
        } else {
5927
            $forceType = false;
5928
        }
5929
5930
        $xml = '';
5931
        if (mb_strpos($type, ':')) {
5932
            $uqType = mb_substr($type, mb_strrpos($type, ':') + 1);
5933
            $ns     = mb_substr($type, 0, mb_strrpos($type, ':'));
5934
            $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5935
            if ($this->getNamespaceFromPrefix($ns)) {
5936
                $ns = $this->getNamespaceFromPrefix($ns);
5937
                $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5938
            }
5939
5940
            if ($ns === $this->XMLSchemaVersion || 'http://schemas.xmlsoap.org/soap/encoding/' === $ns) {
5941
                $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5942
                $elementNS = '';
5943
                if ($unqualified && 'literal' === $use) {
5944
                    $elementNS = ' xmlns=""';
5945
                }
5946
                if (null === $value) {
5947
                    if ('literal' === $use) {
5948
                        // TODO: depends on minOccurs
5949
                        $xml = "<$name$elementNS>";
5950
                    } else {
5951
                        // TODO: depends on nillable, which should be checked before calling this method
5952
                        $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

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

6124
                           . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
Loading history...
6125
                           . ':Array" '
6126
                           . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6127
                           . ':arrayType="'
6128
                           . $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

6128
                           . /** @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

6128
                           . $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($typeDef['arrayType']))
Loading history...
6129
                           . ':'
6130
                           . $this->getLocalPart($typeDef['arrayType'])
6131
                           . '[0]"/>';
6132
                }
6133
                $this->debug("in serializeType: returning: $xml");
6134
6135
                return $xml;
6136
            }
6137
            if (isset($typeDef['multidimensional'])) {
6138
                $nv = [];
6139
                foreach ($value as $v) {
6140
                    $cols = ',' . count($v);
6141
                    $nv   = array_merge($nv, $v);
6142
                }
6143
                $value = $nv;
6144
            } else {
6145
                $cols = '';
6146
            }
6147
            if (is_array($value) && count($value) >= 1) {
6148
                $rows     = count($value);
6149
                $contents = '';
6150
                foreach ($value as $k => $v) {
6151
                    $this->debug("serializing array element: $k, " . (is_array($v) ? 'array' : $v) . " of type: $typeDef[arrayType]");
6152
                    //if (strpos($typeDef['arrayType'], ':') ) {
6153
                    if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'], true)) {
6154
                        $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6155
                    } else {
6156
                        $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6157
                    }
6158
                }
6159
            } else {
6160
                $rows     = 0;
6161
                $contents = null;
6162
            }
6163
            // TODO: for now, an empty value will be serialized as a zero element
6164
            // array.  Revisit this when coding the handling of null/nil values.
6165
            if ('literal' === $use) {
6166
                $xml = "<$name$elementNS>" . $contents . "</$name>";
6167
            } else {
6168
                $xml = "<$name$elementNS xsi:type=\""
6169
                       . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6170
                       . ':Array" '
6171
                       . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6172
                       . ':arrayType="'
6173
                       . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6174
                       . ':'
6175
                       . $this->getLocalPart($typeDef['arrayType'])
6176
                       . "[$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...
6177
                       . $contents
6178
                       . "</$name>";
6179
            }
6180
        } elseif ('scalar' === $phpType) {
6181
            if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6182
                $elementNS = " xmlns=\"$ns\"";
6183
            } else {
6184
                if ($unqualified) {
6185
                    $elementNS = ' xmlns=""';
6186
                } else {
6187
                    $elementNS = '';
6188
                }
6189
            }
6190
            if ('literal' === $use) {
6191
                if ($forceType) {
6192
                    $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6193
                } else {
6194
                    $xml = "<$name$elementNS>$value</$name>";
6195
                }
6196
            } else {
6197
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6198
            }
6199
        }
6200
        $this->debug("in serializeType: returning: $xml");
6201
6202
        return $xml;
6203
    }
6204
6205
    /**
6206
     * Serializes the attributes for a complexType
6207
     *
6208
     * @param array  $typeDef our internal representation of an XML schema type (or element)
6209
     * @param mixed  $value   a native PHP value (parameter value)
6210
     * @param string $ns      the namespace of the type
6211
     * @param string $uqType  the local part of the type
6212
     * @return string value serialized as an XML string
6213
     */
6214
    private function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType)
6215
    {
6216
        $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6217
        $xml = '';
6218
        if (isset($typeDef['extensionBase'])) {
6219
            $nsx     = $this->getPrefix($typeDef['extensionBase']);
6220
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6221
            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

6221
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6222
                $nsx = $this->getNamespaceFromPrefix($nsx);
6223
            }
6224
            if (false !== ($typeDefx = $this->getTypeDef($uqTypex, $nsx))) {
6225
                $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6226
                $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6227
            } else {
6228
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6229
            }
6230
        }
6231
        if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6232
            $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6233
            if (is_array($value)) {
6234
                $xvalue = $value;
6235
            } elseif (is_object($value)) {
6236
                $xvalue = get_object_vars($value);
6237
            } else {
6238
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6239
                $xvalue = [];
6240
            }
6241
            foreach ($typeDef['attrs'] as $aName => $attrs) {
6242
                if (isset($xvalue['!' . $aName])) {
6243
                    $xname = '!' . $aName;
6244
                    $this->debug("value provided for attribute $aName with key $xname");
6245
                } elseif (isset($xvalue[$aName])) {
6246
                    $xname = $aName;
6247
                    $this->debug("value provided for attribute $aName with key $xname");
6248
                } elseif (isset($attrs['default'])) {
6249
                    $xname          = '!' . $aName;
6250
                    $xvalue[$xname] = $attrs['default'];
6251
                    $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6252
                } else {
6253
                    $xname = '';
6254
                    $this->debug("no value provided for attribute $aName");
6255
                }
6256
                if ($xname) {
6257
                    $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . '"';
6258
                }
6259
            }
6260
        } else {
6261
            $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6262
        }
6263
6264
        return $xml;
6265
    }
6266
6267
    /**
6268
     * Serializes the elements for a complexType
6269
     *
6270
     * @param array       $typeDef       our internal representation of an XML schema type (or element)
6271
     * @param mixed       $value         a native PHP value (parameter value)
6272
     * @param string      $ns            the namespace of the type
6273
     * @param string      $uqType        the local part of the type
6274
     * @param string      $use           use for part (encoded|literal)
6275
     * @param bool|string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6276
     * @return string value serialized as an XML string
6277
     */
6278
    private function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use = 'encoded', $encodingStyle = false)
6279
    {
6280
        $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6281
        $xml = '';
6282
        if (isset($typeDef['extensionBase'])) {
6283
            $nsx     = $this->getPrefix($typeDef['extensionBase']);
6284
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6285
            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

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

6678
    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...
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...
6679
    {
6680
        // position in a total number of elements, starting from 0
6681
        // update class level pos
6682
        $pos = $this->position++;
6683
        // and set mine
6684
        $this->message[$pos] = ['pos' => $pos, 'children' => '', 'cdata' => ''];
6685
        // depth = how many levels removed from root?
6686
        // set mine as current global depth and increment global depth value
6687
        $this->message[$pos]['depth'] = $this->depth++;
6688
        // else add self as child to whoever the current parent is
6689
        if (0 !== $pos) {
6690
            $this->message[$this->parent]['children'] .= '|' . $pos;
6691
        }
6692
        // set my parent
6693
        $this->message[$pos]['parent'] = $this->parent;
6694
        // set self as current parent
6695
        $this->parent = $pos;
6696
        // set self as current value for this depth
6697
        $this->depth_array[$this->depth] = $pos;
6698
        // get element prefix
6699
        if (mb_strpos($name, ':')) {
6700
            // get ns prefix
6701
            $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
6702
            // get unqualified name
6703
            $name = mb_substr(mb_strstr($name, ':'), 1);
6704
        }
6705
        // set status
6706
        if ('Envelope' === $name && '' === $this->status) {
6707
            $this->status = 'envelope';
6708
        } elseif ('Header' === $name && 'envelope' === $this->status) {
6709
            $this->root_header = $pos;
6710
            $this->status      = 'header';
6711
        } elseif ('Body' === $name && 'envelope' === $this->status) {
6712
            $this->status        = 'body';
6713
            $this->body_position = $pos;
6714
        // set method
6715
        } elseif ('body' === $this->status && $pos === ($this->body_position + 1)) {
6716
            $this->status                = 'method';
6717
            $this->root_struct_name      = $name;
6718
            $this->root_struct           = $pos;
6719
            $this->message[$pos]['type'] = 'struct';
6720
            $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6721
        }
6722
        // set my status
6723
        $this->message[$pos]['status'] = $this->status;
6724
        // set name
6725
        $this->message[$pos]['name'] = htmlspecialchars($name);
6726
        // set attrs
6727
        $this->message[$pos]['attrs'] = $attrs;
6728
        // loop through atts, logging ns and type declarations
6729
        $attstr = '';
6730
        foreach ($attrs as $key => $value) {
6731
            $key_prefix    = $this->getPrefix($key);
6732
            $key_localpart = $this->getLocalPart($key);
6733
            // if ns declarations, add to class level array of valid namespaces
6734
            if ('xmlns' === $key_prefix) {
6735
                if (preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) {
6736
                    $this->XMLSchemaVersion  = $value;
6737
                    $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6738
                    $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
6739
                }
6740
                $this->namespaces[$key_localpart] = $value;
6741
                // set method namespace
6742
                if ($name === $this->root_struct_name) {
6743
                    $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...
6744
                }
6745
                // if it's a type declaration, set type
6746
            } elseif ('type' === $key_localpart) {
6747
                if (isset($this->message[$pos]['type']) && 'array' === $this->message[$pos]['type']) {
6748
                    // do nothing: already processed arrayType
6749
                } else {
6750
                    $value_prefix                      = $this->getPrefix($value);
6751
                    $value_localpart                   = $this->getLocalPart($value);
6752
                    $this->message[$pos]['type']       = $value_localpart;
6753
                    $this->message[$pos]['typePrefix'] = $value_prefix;
6754
                    if (isset($this->namespaces[$value_prefix])) {
6755
                        $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6756
                    } 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

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

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

6937
    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...
6938
    {
6939
        $pos = $this->depth_array[$this->depth];
6940
        if ('UTF-8' === $this->xml_encoding) {
6941
            // TODO: add an option to disable this for folks who want
6942
            // raw UTF-8 that, e.g., might not map to iso-8859-1
6943
            // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6944
            if ($this->decode_utf8) {
6945
                $data = utf8_decode($data);
6946
            }
6947
        }
6948
        $this->message[$pos]['cdata'] .= $data;
6949
        // for doclit
6950
        if ('header' === $this->status) {
6951
            $this->responseHeaders .= $data;
6952
        } else {
6953
            $this->document .= $data;
6954
        }
6955
    }
6956
6957
    /**
6958
     * Get the parsed message (SOAP Body)
6959
     *
6960
     * @return    mixed
6961
     * @deprecated    use get_soapbody instead
6962
     */
6963
    public function get_response()
6964
    {
6965
        return $this->soapresponse;
6966
    }
6967
6968
    /**
6969
     * Get the parsed SOAP Body (null if there was none)
6970
     *
6971
     * @return    mixed
6972
     */
6973
    public function get_soapbody()
6974
    {
6975
        return $this->soapresponse;
6976
    }
6977
6978
    /**
6979
     * Get the parsed SOAP Header (null if there was none)
6980
     *
6981
     * @return    mixed
6982
     */
6983
    public function get_soapheader()
6984
    {
6985
        return $this->soapheader;
6986
    }
6987
6988
    /**
6989
     * Get the unparsed SOAP Header
6990
     *
6991
     * @return    string XML or empty if no Header
6992
     */
6993
    public function getHeaders()
6994
    {
6995
        return $this->responseHeaders;
6996
    }
6997
6998
    /**
6999
     * Decodes simple types into PHP variables
7000
     *
7001
     * @param    string $value  value to decode
7002
     * @param    string $type   XML type to decode
7003
     * @param    string $typens XML type namespace to decode
7004
     * @return    mixed PHP value
7005
     */
7006
    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

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

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