Nusoap_parser::buildVal()   F
last analyzed

Complexity

Conditions 38
Paths 204

Size

Total Lines 110
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 74
dl 0
loc 110
rs 3.2833
c 2
b 1
f 0
cc 38
nc 204
nop 1

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
    /**
130
     * Set schema version
131
     *
132
     * @var string
133
     */
134
    public $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
135
136
    /**
137
     * Charset encoding for outgoing messages
138
     *
139
     * @var string
140
     */
141
    public $soap_defencoding = 'ISO-8859-1';
142
    //var $soap_defencoding = 'UTF-8';
143
144
    /**
145
     * Namespaces in an array of prefix => uri
146
     *
147
     * This is "seeded" by a set of constants, but it may be altered by code
148
     *
149
     * @var array
150
     */
151
    public $namespaces = [
152
        'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
153
        'xsd'      => 'http://www.w3.org/2001/XMLSchema',
154
        'xsi'      => 'http://www.w3.org/2001/XMLSchema-instance',
155
        'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
156
    ];
157
158
    /**
159
     * Namespaces used in the current context, e.g. during serialization
160
     *
161
     * @var array
162
     */
163
    protected $usedNamespaces = [];
164
165
    /**
166
     * XML Schema types in an array of uri => (array of xml type => php type)
167
     * is this legacy yet?
168
     * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
169
     * @var array
170
     */
171
    public $typemap = [
172
        'http://www.w3.org/2001/XMLSchema'          => [
173
            'string'             => 'string',
174
            'boolean'            => 'boolean',
175
            'float'              => 'double',
176
            'double'             => 'double',
177
            'decimal'            => 'double',
178
            'duration'           => '',
179
            'dateTime'           => 'string',
180
            'time'               => 'string',
181
            'date'               => 'string',
182
            'gYearMonth'         => '',
183
            'gYear'              => '',
184
            'gMonthDay'          => '',
185
            'gDay'               => '',
186
            'gMonth'             => '',
187
            'hexBinary'          => 'string',
188
            'base64Binary'       => 'string',
189
            // abstract "any" types
190
            'anyType'            => 'string',
191
            'anySimpleType'      => 'string',
192
            // derived datatypes
193
            'normalizedString'   => 'string',
194
            'token'              => 'string',
195
            'language'           => '',
196
            'NMTOKEN'            => '',
197
            'NMTOKENS'           => '',
198
            'Name'               => '',
199
            'NCName'             => '',
200
            'ID'                 => '',
201
            'IDREF'              => '',
202
            'IDREFS'             => '',
203
            'ENTITY'             => '',
204
            'ENTITIES'           => '',
205
            'integer'            => 'integer',
206
            'nonPositiveInteger' => 'integer',
207
            'negativeInteger'    => 'integer',
208
            'long'               => 'integer',
209
            'int'                => 'integer',
210
            'short'              => 'integer',
211
            'byte'               => 'integer',
212
            'nonNegativeInteger' => 'integer',
213
            'unsignedLong'       => '',
214
            'unsignedInt'        => '',
215
            'unsignedShort'      => '',
216
            'unsignedByte'       => '',
217
            'positiveInteger'    => '',
218
        ],
219
        'http://www.w3.org/2000/10/XMLSchema'       => [
220
            'i4'           => '',
221
            'int'          => 'integer',
222
            'boolean'      => 'boolean',
223
            'string'       => 'string',
224
            'double'       => 'double',
225
            'float'        => 'double',
226
            'dateTime'     => 'string',
227
            'timeInstant'  => 'string',
228
            'base64Binary' => 'string',
229
            'base64'       => 'string',
230
            'ur-type'      => 'array',
231
        ],
232
        'http://www.w3.org/1999/XMLSchema'          => [
233
            'i4'           => '',
234
            'int'          => 'integer',
235
            'boolean'      => 'boolean',
236
            'string'       => 'string',
237
            'double'       => 'double',
238
            'float'        => 'double',
239
            'dateTime'     => 'string',
240
            'timeInstant'  => 'string',
241
            'base64Binary' => 'string',
242
            'base64'       => 'string',
243
            'ur-type'      => 'array',
244
        ],
245
        'http://soapinterop.org/xsd'                => ['SOAPStruct' => 'struct'],
246
        'http://schemas.xmlsoap.org/soap/encoding/' => [
247
            'base64' => 'string',
248
            'array'  => 'array',
249
            'Array'  => 'array',
250
        ],
251
        'http://xml.apache.org/xml-soap'            => ['Map'],
252
    ];
253
254
    /**
255
     * XML entities to convert
256
     *
257
     * @var array
258
     * @deprecated
259
     * @see      expandEntities
260
     */
261
    public $xmlEntities = [
262
        'quot' => '"',
263
        'amp'  => '&',
264
        'lt'   => '<',
265
        'gt'   => '>',
266
        'apos' => "'",
267
    ];
268
269
    /**
270
     * Constructor
271
     */
272
    public function __construct()
273
    {
274
        $this->debugLevel = $GLOBALS['_transient']['static']['Nusoap_base']['globalDebugLevel'];
275
    }
276
277
    /**
278
     * Gets the global debug level, which applies to future instances
279
     *
280
     * @return int Debug level 0-9, where 0 turns off
281
     */
282
    public function getGlobalDebugLevel()
283
    {
284
        return $GLOBALS['_transient']['static']['Nusoap_base']['globalDebugLevel'];
285
    }
286
287
    /**
288
     * Sets the global debug level, which applies to future instances
289
     *
290
     * @param int $level Debug level 0-9, where 0 turns off
291
     */
292
    public function setGlobalDebugLevel($level)
293
    {
294
        $GLOBALS['_transient']['static']['Nusoap_base']['globalDebugLevel'] = $level;
295
    }
296
297
    /**
298
     * Gets the debug level for this instance
299
     *
300
     * @return int Debug level 0-9, where 0 turns off
301
     */
302
    public function getDebugLevel()
303
    {
304
        return $this->debugLevel;
305
    }
306
307
    /**
308
     * Sets the debug level for this instance
309
     *
310
     * @param int $level Debug level 0-9, where 0 turns off
311
     */
312
    public function setDebugLevel($level)
313
    {
314
        $this->debugLevel = $level;
315
    }
316
317
    /**
318
     * Adds debug data to the instance debug string with formatting
319
     *
320
     * @param string $string debug data
321
     */
322
    protected function debug($string)
323
    {
324
        if ($this->debugLevel > 0) {
325
            $this->appendDebug($this->getmicrotime() . ' ' . get_class($this) . ": $string\n");
326
        }
327
    }
328
329
    /**
330
     * Adds debug data to the instance debug string without formatting
331
     *
332
     * @param string $string debug data
333
     */
334
    public function appendDebug($string)
335
    {
336
        if ($this->debugLevel > 0) {
337
            // it would be nice to use a memory stream here to use
338
            // memory more efficiently
339
            $this->debug_str .= $string;
340
        }
341
    }
342
343
    /**
344
     * Clears the current debug data for this instance
345
     */
346
    public function clearDebug()
347
    {
348
        // it would be nice to use a memory stream here to use
349
        // memory more efficiently
350
        $this->debug_str = '';
351
    }
352
353
    /**
354
     * Gets the current debug data for this instance
355
     *
356
     * @return   string debug data
357
     */
358
    public function &getDebug()
359
    {
360
        // it would be nice to use a memory stream here to use
361
        // memory more efficiently
362
        return $this->debug_str;
363
    }
364
365
    /**
366
     * Gets the current debug data for this instance as an XML comment
367
     * this may change the contents of the debug data
368
     *
369
     * @return   string debug data as an XML comment
370
     */
371
    public function &getDebugAsXMLComment()
372
    {
373
        // it would be nice to use a memory stream here to use
374
        // memory more efficiently
375
        while (mb_strpos($this->debug_str, '--')) {
376
            $this->debug_str = str_replace('--', '- -', $this->debug_str);
377
        }
378
        $ret = "<!--\n" . $this->debug_str . "\n-->";
379
380
        return $ret;
381
    }
382
383
    /**
384
     * Expands entities, e.g. changes '<' to '&lt;'.
385
     *
386
     * @param string $val The string in which to expand entities.
387
     * @return mixed|string
388
     */
389
    protected function expandEntities($val)
390
    {
391
        if ($this->charencoding) {
392
            $val = str_replace('&', '&amp;', $val);
393
            $val = str_replace("'", '&apos;', $val);
394
            $val = str_replace('"', '&quot;', $val);
395
            $val = str_replace('<', '&lt;', $val);
396
            $val = str_replace('>', '&gt;', $val);
397
        }
398
399
        return $val;
400
    }
401
402
    /**
403
     * Returns error string if present
404
     *
405
     * @return mixed error string or false
406
     */
407
    public function getError()
408
    {
409
        if ('' !== $this->error_str) {
410
            return $this->error_str;
411
        }
412
413
        return false;
414
    }
415
416
    /**
417
     * Sets error string
418
     *
419
     * @param string $str
420
     */
421
    protected function setError($str)
422
    {
423
        $this->error_str = $str;
424
    }
425
426
    /**
427
     * Detect if array is a simple array or a struct (associative array)
428
     *
429
     * @param  mixed $val The PHP array
430
     * @return string (arraySimple|arrayStruct)
431
     */
432
    protected function isArraySimpleOrStruct($val)
433
    {
434
        $keyList = array_keys($val);
435
        foreach ($keyList as $keyListValue) {
436
            if (!is_int($keyListValue)) {
437
                return 'arrayStruct';
438
            }
439
        }
440
441
        return 'arraySimple';
442
    }
443
444
    /**
445
     * Serializes PHP values in accordance w/ section 5. Type information is
446
     * not serialized if $use == 'literal'.
447
     *
448
     * @param  mixed            $val        The value to serialize
449
     * @param  bool|string      $name       The name (local part) of the XML element
450
     * @param  bool|string      $type       The XML schema type (local part) for the element
451
     * @param  bool|string      $name_ns    The namespace for the name of the XML element
452
     * @param  bool|string      $type_ns    The namespace for the type of the element
453
     * @param bool|string|array $attributes The attributes to serialize as name=>value pairs
454
     * @param  string           $use        The WSDL "use" (encoded|literal)
455
     * @param    bool           $soapval    Whether this is called from Soapval.
456
     * @return string      The serialized element, possibly with child elements
457
     */
458
    public function serialize_val(
459
        $val,
460
        $name = false,
461
        $type = false,
462
        $name_ns = false,
463
        $type_ns = false,
464
        $attributes = false,
465
        $use = 'encoded',
466
        $soapval = false)
467
    {
468
        $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, Soapval=$soapval");
469
        $this->appendDebug('value=' . $this->varDump($val));
470
        $this->appendDebug('attributes=' . $this->varDump($attributes));
471
        if (is_object($val) && 'Soapval' === get_class($val) && (!$soapval)) {
472
            $this->debug('serialize_val: serialize Soapval');
473
            $xml = $val->serialize($use);
474
            $this->appendDebug($val->getDebug());
475
            $val->clearDebug();
476
            $this->debug("serialize_val of Soapval returning $xml");
477
478
            return $xml;
479
        }
480
        // force valid name if necessary
481
        if (is_numeric($name)) {
482
            $name = '__numeric_' . $name;
483
        } elseif (!$name) {
484
            $name = 'noname';
485
        }
486
        // if name has ns, add ns prefix to name
487
        $xmlns = '';
488
        if ($name_ns) {
489
            $prefix = 'nu' . mt_rand(1000, 9999);
490
            $name   = $prefix . ':' . $name;
491
            $xmlns  .= " xmlns:$prefix=\"$name_ns\"";
492
        }
493
        // if type is prefixed, create type prefix
494
        if ('' !== $type_ns && $type_ns === $this->namespaces['xsd']) {
495
            // need to fix this. shouldn't default to xsd if no ns specified
496
            // w/o checking against typemap
497
            $type_prefix = 'xsd';
498
        } elseif ($type_ns) {
499
            $type_prefix = 'ns' . mt_rand(1000, 9999);
500
            $xmlns       .= " xmlns:$type_prefix=\"$type_ns\"";
501
        }
502
        // serialize attributes if present
503
        $atts = '';
504
        if ($attributes) {
505
            foreach ($attributes as $k => $v) {
506
                $atts .= " $k=\"" . $this->expandEntities($v) . '"';
507
            }
508
        }
509
        // serialize null value
510
        if (null === $val) {
511
            $this->debug('serialize_val: serialize null');
512
            if ('literal' === $use) {
513
                // TODO: depends on minOccurs
514
                $xml = "<$name$xmlns$atts>";
515
                $this->debug("serialize_val returning $xml");
516
517
                return $xml;
518
            }
519
            if (isset($type) && isset($type_prefix)) {
520
                $type_str = " xsi:type=\"$type_prefix:$type\"";
521
            } else {
522
                $type_str = '';
523
            }
524
            $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\">";
525
            $this->debug("serialize_val returning $xml");
526
527
            return $xml;
528
        }
529
        // serialize if an xsd built-in primitive type
530
        if ('' !== $type && isset($this->typemap[$this->XMLSchemaVersion][$type])) {
531
            $this->debug('serialize_val: serialize xsd built-in primitive type');
532
            if (is_bool($val)) {
533
                if ('boolean' === $type) {
534
                    $val = $val ? 'true' : 'false';
535
                } elseif (!$val) {
536
                    $val = 0;
537
                }
538
            } elseif (is_string($val)) {
539
                $val = $this->expandEntities($val);
540
            }
541
            if ('literal' === $use) {
542
                $xml = "<$name$xmlns$atts>$val</$name>";
543
                $this->debug("serialize_val returning $xml");
544
545
                return $xml;
546
            }
547
            $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
548
            $this->debug("serialize_val returning $xml");
549
550
            return $xml;
551
        }
552
        // detect type and serialize
553
        $xml = '';
554
        switch (true) {
555
            case (is_bool($val) || 'boolean' === $type):
556
                $this->debug('serialize_val: serialize boolean');
557
                if ('boolean' === $type) {
558
                    $val = $val ? 'true' : 'false';
559
                } elseif (!$val) {
560
                    $val = 0;
561
                }
562
                if ('literal' === $use) {
563
                    $xml .= "<$name$xmlns$atts>$val</$name>";
564
                } else {
565
                    $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
566
                }
567
568
                break;
569
            case (is_int($val) || is_int($val) || 'int' === $type):
570
                $this->debug('serialize_val: serialize int');
571
                if ('literal' === $use) {
572
                    $xml .= "<$name$xmlns$atts>$val</$name>";
573
                } else {
574
                    $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
575
                }
576
577
                break;
578
            case (is_float($val) || is_float($val) || 'float' === $type):
579
                $this->debug('serialize_val: serialize float');
580
                if ('literal' === $use) {
581
                    $xml .= "<$name$xmlns$atts>$val</$name>";
582
                } else {
583
                    $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
584
                }
585
586
                break;
587
            case (is_string($val) || 'string' === $type):
588
                $this->debug('serialize_val: serialize string');
589
                $val = $this->expandEntities($val);
590
                if ('literal' === $use) {
591
                    $xml .= "<$name$xmlns$atts>$val</$name>";
592
                } else {
593
                    $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
594
                }
595
596
                break;
597
            case is_object($val):
598
                $this->debug('serialize_val: serialize object');
599
                if ('Soapval' === get_class($val)) {
600
                    $this->debug('serialize_val: serialize Soapval object');
601
                    $pXml = $val->serialize($use);
602
                    $this->appendDebug($val->getDebug());
603
                    $val->clearDebug();
604
                } else {
605
                    if (!$name) {
606
                        $name = get_class($val);
607
                        $this->debug("In serialize_val, used class name $name as element name");
608
                    } else {
609
                        $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
610
                    }
611
                    foreach (get_object_vars($val) as $k => $v) {
612
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
613
                    }
614
                }
615
                $type_str = '';
616
                if (isset($type) && isset($type_prefix)) {
617
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
618
                }
619
                if ('literal' === $use) {
620
                    $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...
621
                } else {
622
                    $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
623
                }
624
625
                break;
626
            case (is_array($val) || $type):
627
                // detect if struct or array
628
                $array_types = [];
629
                $tt_ns       = $tt = '';
630
                $valueType   = $this->isArraySimpleOrStruct($val);
631
                if ('arraySimple' === $valueType || preg_match('/^ArrayOf/', $type)) {
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type boolean; however, parameter $subject of preg_match() 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

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

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

1200
        $this->namespaces          = array_merge($this->namespaces, /** @scrutinizer ignore-type */ $namespaces);
Loading history...
1201
        // parse schema file
1202
        if ('' !== $schema) {
1203
            $this->debug('initial schema file: ' . $schema);
1204
            $this->parseFile($schema, 'schema');
1205
        }
1206
1207
        // parse xml file
1208
        if ('' !== $xml) {
1209
            $this->debug('initial xml file: ' . $xml);
1210
            $this->parseFile($xml, 'xml');
1211
        }
1212
    }
1213
1214
    /**
1215
     * Parse an XML file
1216
     *
1217
     * @param  string $xml  path/URL to XML file
1218
     * @param  string $type (schema | xml)
1219
     * @return bool
1220
     */
1221
    public function parseFile($xml, $type)
1222
    {
1223
        // parse xml file
1224
        if ('' !== $xml) {
1225
            $xmlStr = @file_get_contents($xml);
1226
            if ('' === $xmlStr) {
1227
                $msg = 'Error reading XML from ' . $xml;
1228
                $this->setError($msg);
1229
                $this->debug($msg);
1230
1231
                return false;
1232
            }
1233
1234
            $this->debug("parsing $xml");
1235
            $this->parseString($xmlStr, $type);
0 ignored issues
show
Bug introduced by
It seems like $xmlStr can also be of type false; however, parameter $xml of Nusoap_xmlschema::parseString() 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

1235
            $this->parseString(/** @scrutinizer ignore-type */ $xmlStr, $type);
Loading history...
1236
            $this->debug("done parsing $xml");
1237
1238
            return true;
1239
        }
1240
1241
        return false;
1242
    }
1243
1244
    /**
1245
     * Parse an XML string
1246
     *
1247
     * @param string $xml  path or URL
1248
     * @param string $type (schema|xml)
1249
     */
1250
    private function parseString($xml, $type)
1251
    {
1252
        // parse xml string
1253
        if ('' !== $xml) {
1254
            // Create an XML parser.
1255
            $this->parser = xml_parser_create();
1256
            // Set the options for parsing the XML data.
1257
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1258
            // Set the object for the parser.
1259
            xml_set_object($this->parser, $this);
1260
            // Set the element handlers for the parser.
1261
            if ('schema' === $type) {
1262
                xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
1263
                xml_set_character_data_handler($this->parser, 'schemaCharacterData');
1264
            } elseif ('xml' === $type) {
1265
                xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
1266
                xml_set_character_data_handler($this->parser, 'xmlCharacterData');
1267
            }
1268
1269
            // Parse the XML file.
1270
            if (!xml_parse($this->parser, $xml, true)) {
1271
                // Display an error message.
1272
                $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)));
1273
                $this->debug($errstr);
1274
                $this->debug("XML payload:\n" . $xml);
1275
                $this->setError($errstr);
1276
            }
1277
1278
            xml_parser_free($this->parser);
1279
            unset($this->parser);
1280
        } else {
1281
            $this->debug('no xml passed to parseString()!!');
1282
            $this->setError('no xml passed to parseString()!!');
1283
        }
1284
    }
1285
1286
    /**
1287
     * Gets a type name for an unnamed type
1288
     * @param    string $ename Element name
1289
     * @return string A type name for an unnamed type
1290
     */
1291
    private function CreateTypeName($ename)
1292
    {
1293
        $scope = '';
1294
        for ($i = 0, $iMax = count($this->complexTypeStack); $i < $iMax; ++$i) {
1295
            $scope .= $this->complexTypeStack[$i] . '_';
1296
        }
1297
1298
        return $scope . $ename . '_ContainedType';
1299
    }
1300
1301
    /**
1302
     * Start-element handler
1303
     *
1304
     * @param string          $parser XML parser object
1305
     * @param string          $name   element name
1306
     * @param    string|array $attrs  associative array of attributes
1307
     */
1308
    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

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

1661
    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...
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...
1662
    {
1663
        // bring depth down a notch
1664
        $this->depth--;
1665
        // position of current element is equal to the last value left in depth_array for my depth
1666
        if (isset($this->depth_array[$this->depth])) {
1667
            $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...
1668
        }
1669
        // get element prefix
1670
        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...
1671
            // get unqualified name
1672
            $name = $this->getLocalPart($name);
1673
        } else {
1674
            $prefix = '';
1675
        }
1676
        // move on...
1677
        if ('complexType' === $name) {
1678
            $this->xdebug('done processing complexType ' . ($this->currentComplexType ?: '(unknown)'));
1679
            $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1680
            $this->currentComplexType = array_pop($this->complexTypeStack);
1681
            //$this->currentElement = false;
1682
        }
1683
        if ('element' === $name) {
1684
            $this->xdebug('done processing element ' . ($this->currentElement ?: '(unknown)'));
1685
            $this->currentElement = array_pop($this->elementStack);
1686
        }
1687
        if ('simpleType' === $name) {
1688
            $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ?: '(unknown)'));
1689
            $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1690
            $this->currentSimpleType = array_pop($this->simpleTypeStack);
1691
        }
1692
    }
1693
1694
    /**
1695
     * Element content handler
1696
     *
1697
     * @param string $parser XML parser object
1698
     * @param string $data   element content
1699
     */
1700
    private function schemaCharacterData($parser, $data)
0 ignored issues
show
Unused Code introduced by
The method schemaCharacterData() is not used, and could be removed.

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

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

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

1700
    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...
1701
    {
1702
        $pos                          = $this->depth_array[$this->depth - 1];
1703
        $this->message[$pos]['cdata'] .= $data;
1704
    }
1705
1706
    /**
1707
     * Serialize the schema
1708
     */
1709
    public function serializeSchema()
1710
    {
1711
        $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1712
        $xml          = '';
1713
        // imports
1714
        if ($this->imports && is_array($this->imports)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->imports 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...
1715
            foreach ($this->imports as $ns => $list) {
1716
                foreach ($list as $ii) {
1717
                    if ('' !== $ii['location']) {
1718
                        $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\">\n";
1719
                    } else {
1720
                        $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\">\n";
1721
                    }
1722
                }
1723
            }
1724
        }
1725
        // complex types
1726
        foreach ($this->complexTypes as $typeName => $attrs) {
1727
            $contentStr = '';
1728
            // serialize child elements
1729
            if (isset($attrs['elements']) && (count($attrs['elements']) > 0)) {
1730
                foreach ($attrs['elements'] as $element => $eParts) {
1731
                    if (isset($eParts['ref'])) {
1732
                        $contentStr .= "   <$schemaPrefix:element ref=\"$element\">\n";
1733
                    } else {
1734
                        $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . '"';
1735
                        foreach ($eParts as $aName => $aValue) {
1736
                            // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1737
                            if ('name' !== $aName && 'type' !== $aName) {
1738
                                $contentStr .= " $aName=\"$aValue\"";
1739
                            }
1740
                        }
1741
                        $contentStr .= ">\n";
1742
                    }
1743
                }
1744
                // compositor wraps elements
1745
                if (isset($attrs['compositor']) && ('' !== $attrs['compositor'])) {
1746
                    $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n" . $contentStr . "  </$schemaPrefix:$attrs[compositor]>\n";
1747
                }
1748
            }
1749
            // attributes
1750
            if (isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)) {
1751
                foreach ($attrs['attrs'] as $attr => $aParts) {
1752
                    $contentStr .= "    <$schemaPrefix:attribute";
1753
                    foreach ($aParts as $a => $v) {
1754
                        if ('ref' === $a || 'type' === $a) {
1755
                            $contentStr .= " $a=\"" . $this->contractQName($v) . '"';
1756
                        } elseif ('http://schemas.xmlsoap.org/wsdl/:arrayType' === $a) {
1757
                            $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1758
                            $contentStr                   .= ' Wsdl:arrayType="' . $this->contractQName($v) . '"';
1759
                        } else {
1760
                            $contentStr .= " $a=\"$v\"";
1761
                        }
1762
                    }
1763
                    $contentStr .= ">\n";
1764
                }
1765
            }
1766
            // if restriction
1767
            if (isset($attrs['restrictionBase']) && '' !== $attrs['restrictionBase']) {
1768
                $contentStr = "   <$schemaPrefix:restriction base=\"" . $this->contractQName($attrs['restrictionBase']) . "\">\n" . $contentStr . "   </$schemaPrefix:restriction>\n";
1769
                // complex or simple content
1770
                if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)) {
1771
                    $contentStr = "  <$schemaPrefix:complexContent>\n" . $contentStr . "  </$schemaPrefix:complexContent>\n";
1772
                }
1773
            }
1774
            // finalize complex type
1775
            if ('' !== $contentStr) {
1776
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n" . $contentStr . " </$schemaPrefix:complexType>\n";
1777
            } else {
1778
                $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n";
1779
            }
1780
            $xml .= $contentStr;
1781
        }
1782
        // simple types
1783
        if (isset($this->simpleTypes) && count($this->simpleTypes) > 0) {
1784
            foreach ($this->simpleTypes as $typeName => $eParts) {
1785
                $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"" . $this->contractQName($eParts['type']) . "\">\n";
1786
                if (isset($eParts['enumeration'])) {
1787
                    foreach ($eParts['enumeration'] as $e) {
1788
                        $xml .= "  <$schemaPrefix:enumeration value=\"$e\">\n";
1789
                    }
1790
                }
1791
                $xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1792
            }
1793
        }
1794
        // elements
1795
        if (isset($this->elements) && count($this->elements) > 0) {
1796
            foreach ($this->elements as $element => $eParts) {
1797
                $xml .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\">\n";
1798
            }
1799
        }
1800
        // attributes
1801
        if (isset($this->attributes) && count($this->attributes) > 0) {
1802
            foreach ($this->attributes as $attr => $aParts) {
1803
                $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"" . $this->contractQName($aParts['type']) . "\"\n>";
1804
            }
1805
        }
1806
        // finish 'er up
1807
        $attr = '';
1808
        foreach ($this->schemaInfo as $k => $v) {
1809
            if ('elementFormDefault' === $k || 'attributeFormDefault' === $k) {
1810
                $attr .= " $k=\"$v\"";
1811
            }
1812
        }
1813
        $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1814
        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 $excludes 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

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

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

2334
        curl_setopt(/** @scrutinizer ignore-type */ $this->ch, $option, $value);
Loading history...
2335
    }
2336
2337
    /**
2338
     * sets an HTTP header
2339
     *
2340
     * @param string $name  The name of the header
2341
     * @param string $value The value of the header
2342
     */
2343
    private function setHeader($name, $value)
2344
    {
2345
        $this->outgoing_headers[$name] = $value;
2346
        $this->debug("set header $name: $value");
2347
    }
2348
2349
    /**
2350
     * unsets an HTTP header
2351
     *
2352
     * @param string $name The name of the header
2353
     */
2354
    private function unsetHeader($name)
2355
    {
2356
        if (isset($this->outgoing_headers[$name])) {
2357
            $this->debug("unset header $name");
2358
            unset($this->outgoing_headers[$name]);
2359
        }
2360
    }
2361
2362
    /**
2363
     * sets the URL to which to connect
2364
     *
2365
     * @param string $url The URL to which to connect
2366
     */
2367
    private function setURL($url)
2368
    {
2369
        $this->url = $url;
2370
2371
        $u = parse_url($url);
2372
        foreach ($u as $k => $v) {
2373
            $this->debug("parsed URL $k = $v");
2374
            $this->$k = $v;
2375
        }
2376
2377
        // add any GET params to path
2378
        if (isset($u['query']) && '' !== $u['query']) {
2379
            $this->path .= '?' . $u['query'];
2380
        }
2381
2382
        // set default port
2383
        if (!isset($u['port'])) {
2384
            $this->port = 80;
2385
            if ('https' === $u['scheme']) {
2386
                $this->port = 443;
2387
            }
2388
        }
2389
2390
        $this->uri        = $this->path;
2391
        $this->digest_uri = $this->uri;
2392
2393
        // build headers
2394
        if (!isset($u['port'])) {
2395
            $this->setHeader('Host', $this->host);
2396
        } else {
2397
            $this->setHeader('Host', $this->host . ':' . $this->port);
2398
        }
2399
2400
        if (isset($u['user']) && '' !== $u['user']) {
2401
            $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2402
        }
2403
    }
2404
2405
    /**
2406
     * gets the I/O method to use
2407
     *
2408
     * @return string I/O method to use (socket|curl|unknown)
2409
     */
2410
    private function io_method()
2411
    {
2412
        if ($this->use_curl || ('https' === $this->scheme) || ('http' === $this->scheme && 'ntlm' === $this->authtype) || ('http' === $this->scheme && is_array($this->proxy) && 'ntlm' === $this->proxy['authtype'])) {
2413
            return 'curl';
2414
        }
2415
        if (('http' === $this->scheme || 'ssl' === $this->scheme) && 'ntlm' !== $this->authtype && (!is_array($this->proxy) || 'ntlm' !== $this->proxy['authtype'])) {
2416
            return 'socket';
2417
        }
2418
2419
        return 'unknown';
2420
    }
2421
2422
    /**
2423
     * establish an HTTP connection
2424
     *
2425
     * @param int $connection_timeout set connection timeout in seconds
2426
     * @param int $response_timeout   set response timeout in seconds
2427
     * @return bool    true if connected, false if not
2428
     */
2429
    private function connect($connection_timeout = 0, $response_timeout = 30)
2430
    {
2431
        // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2432
        // "regular" socket.
2433
        // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2434
        //       loaded), and until PHP5 stream_get_wrappers is not available.
2435
        //      if ($this->scheme == 'https') {
2436
        //          if (version_compare(PHP_VERSION, '4.3.0') >= 0) {
2437
        //              if (extension_loaded('openssl')) {
2438
        //                  $this->scheme = 'ssl';
2439
        //                  $this->debug('Using SSL over OpenSSL');
2440
        //              }
2441
        //          }
2442
        //      }
2443
        $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2444
        if ('socket' === $this->io_method()) {
2445
            if (!is_array($this->proxy)) {
2446
                $host = $this->host;
2447
                $port = $this->port;
0 ignored issues
show
Unused Code introduced by
The assignment to $port is dead and can be removed.
Loading history...
2448
            } else {
2449
                $host = $this->proxy['host'];
2450
                $port = $this->proxy['port'];
2451
            }
2452
2453
            // use persistent connection
2454
            if ($this->persistentConnection && isset($this->fp) && is_resource($this->fp)) {
2455
                if (!feof($this->fp)) {
2456
                    $this->debug('Re-use persistent connection');
2457
2458
                    return true;
2459
                }
2460
                fclose($this->fp);
2461
                $this->debug('Closed persistent connection at EOF');
2462
            }
2463
2464
            // munge host if using OpenSSL
2465
            if ('ssl' === $this->scheme) {
2466
                $host = 'ssl://' . $host;
2467
            }
2468
            $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2469
2470
            // open socket
2471
            if ($connection_timeout > 0) {
2472
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str, $connection_timeout);
0 ignored issues
show
Bug Best Practice introduced by
The property fp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug Best Practice introduced by
The property errno does not exist on Soap_transport_http. Did you maybe forget to declare it?
Loading history...
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

2472
                $this->fp = @fsockopen($host, /** @scrutinizer ignore-type */ $this->port, $this->errno, $this->error_str, $connection_timeout);
Loading history...
2473
            } else {
2474
                $this->fp = @fsockopen($host, $this->port, $this->errno, $this->error_str);
2475
            }
2476
2477
            // test pointer
2478
            if (!$this->fp) {
2479
                $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2480
                if ($this->errno) {
2481
                    $msg .= ', Error (' . $this->errno . '): ' . $this->error_str;
2482
                } else {
2483
                    $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2484
                }
2485
                $this->debug($msg);
2486
                $this->setError($msg);
2487
2488
                return false;
2489
            }
2490
2491
            // set response timeout
2492
            $this->debug('set response timeout to ' . $response_timeout);
2493
            stream_set_timeout($this->fp, $response_timeout);
2494
2495
            $this->debug('socket connected');
2496
2497
            return true;
2498
        }
2499
2500
        if ('curl' === $this->io_method()) {
2501
            if (!extension_loaded('curl')) {
2502
                //          $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2503
                $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.');
2504
2505
                return false;
2506
            }
2507
            // Avoid warnings when PHP does not have these options
2508
            $CURLOPT_CONNECTIONTIMEOUT = 78;
2509
            if (defined('CURLOPT_CONNECTIONTIMEOUT')) {
2510
                $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...
2511
            }
2512
            $CURLOPT_HTTPAUTH = 107;
2513
            if (defined('CURLOPT_HTTPAUTH')) {
2514
                $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2515
            }
2516
            $CURLOPT_PROXYAUTH = 111;
2517
            if (defined('CURLOPT_PROXYAUTH')) {
2518
                $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2519
            }
2520
            $CURLAUTH_BASIC = 1;
2521
            if (defined('CURLAUTH_BASIC')) {
2522
                $CURLAUTH_BASIC = CURLAUTH_BASIC;
2523
            }
2524
            $CURLAUTH_DIGEST = 2;
2525
            if (defined('CURLAUTH_DIGEST')) {
2526
                $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2527
            }
2528
            $CURLAUTH_NTLM = 8;
2529
            if (defined('CURLAUTH_NTLM')) {
2530
                $CURLAUTH_NTLM = CURLAUTH_NTLM;
2531
            }
2532
2533
            $this->debug('connect using cURL');
2534
            // init CURL
2535
            $this->ch = curl_init();
2536
            // set url
2537
            $hostURL = ('' !== $this->port) ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2538
            // add path
2539
            $hostURL .= $this->path;
2540
            $this->setCurlOption(CURLOPT_URL, $hostURL);
2541
            // follow location headers (re-directs)
2542
            if (ini_get('open_basedir')) {
2543
                $this->debug('open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2544
                $this->debug('open_basedir = ');
2545
                $this->appendDebug($this->varDump(ini_get('open_basedir')));
2546
            } else {
2547
                $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2548
            }
2549
            // ask for headers in the response output
2550
            $this->setCurlOption(CURLOPT_HEADER, 1);
2551
            // ask for the response output as the return value
2552
            $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2553
            // encode
2554
            // We manage this ourselves through headers and encoding
2555
            //      if (function_exists('gzuncompress')) {
2556
            //          $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2557
            //      }
2558
            // persistent connection
2559
            if ($this->persistentConnection) {
2560
                // I believe the following comment is now bogus, having applied to
2561
                // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2562
                // The way we send data, we cannot use persistent connections, since
2563
                // there will be some "junk" at the end of our request.
2564
                //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2565
                $this->persistentConnection = false;
2566
                $this->setHeader('Connection', 'close');
2567
            }
2568
            // set timeouts
2569
            if (0 != $connection_timeout) {
2570
                $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2571
            }
2572
            if (0 != $response_timeout) {
2573
                $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2574
            }
2575
2576
            if ('https' === $this->scheme) {
2577
                $this->debug('set cURL SSL verify options');
2578
                // recent versions of cURL turn on peer/host checking by default,
2579
                // while PHP binaries are not compiled with a default location for the
2580
                // CA cert bundle, so disable peer/host checking.
2581
                //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2582
                $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2583
                $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2584
2585
                // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2586
                if ('certificate' === $this->authtype) {
2587
                    $this->debug('set cURL certificate options');
2588
                    if (isset($this->certRequest['cainfofile'])) {
2589
                        $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2590
                    }
2591
                    if (isset($this->certRequest['verifypeer'])) {
2592
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2593
                    } else {
2594
                        $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2595
                    }
2596
                    if (isset($this->certRequest['verifyhost'])) {
2597
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2598
                    } else {
2599
                        $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2600
                    }
2601
                    if (isset($this->certRequest['sslcertfile'])) {
2602
                        $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2603
                    }
2604
                    if (isset($this->certRequest['sslkeyfile'])) {
2605
                        $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2606
                    }
2607
                    if (isset($this->certRequest['passphrase'])) {
2608
                        $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2609
                    }
2610
                    if (isset($this->certRequest['certpassword'])) {
2611
                        $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2612
                    }
2613
                }
2614
            }
2615
            if ($this->authtype && ('certificate' !== $this->authtype)) {
2616
                if ($this->username) {
2617
                    $this->debug('set cURL username/password');
2618
                    $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2619
                }
2620
                if ('basic' === $this->authtype) {
2621
                    $this->debug('set cURL for Basic authentication');
2622
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2623
                }
2624
                if ('digest' === $this->authtype) {
2625
                    $this->debug('set cURL for digest authentication');
2626
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2627
                }
2628
                if ('ntlm' === $this->authtype) {
2629
                    $this->debug('set cURL for NTLM authentication');
2630
                    $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2631
                }
2632
            }
2633
            if (is_array($this->proxy)) {
2634
                $this->debug('set cURL proxy options');
2635
                if ('' !== $this->proxy['port']) {
2636
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'] . ':' . $this->proxy['port']);
2637
                } else {
2638
                    $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2639
                }
2640
                if ($this->proxy['username'] || $this->proxy['password']) {
2641
                    $this->debug('set cURL proxy authentication options');
2642
                    $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'] . ':' . $this->proxy['password']);
2643
                    if ('basic' === $this->proxy['authtype']) {
2644
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2645
                    }
2646
                    if ('ntlm' === $this->proxy['authtype']) {
2647
                        $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2648
                    }
2649
                }
2650
            }
2651
            $this->debug('cURL connection set up');
2652
2653
            return true;
2654
        }
2655
        $this->setError('Unknown scheme ' . $this->scheme);
2656
        $this->debug('Unknown scheme ' . $this->scheme);
2657
2658
        return false;
2659
    }
2660
2661
    /**
2662
     * sends the SOAP request and gets the SOAP response via HTTP[S]
2663
     *
2664
     * @param  string $data             message data
2665
     * @param int     $timeout          set connection timeout in seconds
2666
     * @param int     $response_timeout set response timeout in seconds
2667
     * @param  array  $cookies          cookies to send
2668
     * @return string  data
2669
     */
2670
    public function send($data, $timeout = 0, $response_timeout = 30, $cookies = null)
2671
    {
2672
        $this->debug('entered send() with data of length: ' . mb_strlen($data));
2673
2674
        $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...
2675
        $tries          = 0;
2676
        while ($this->tryagain) {
2677
            $this->tryagain = false;
2678
            if (++$tries < 2) {
2679
                // make connnection
2680
                if (!$this->connect($timeout, $response_timeout)) {
2681
                    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...
2682
                }
2683
2684
                // send request
2685
                if (!$this->sendRequest($data, $cookies)) {
2686
                    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...
2687
                }
2688
2689
                // get response
2690
                $respdata = $this->getResponse();
2691
            } else {
2692
                $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2693
            }
2694
        }
2695
        $this->debug('end of send()');
2696
2697
        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...
2698
    }
2699
2700
    /**
2701
     * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2702
     *
2703
     * @param  string $data             message data
2704
     * @param int     $timeout          set connection timeout in seconds
2705
     * @param int     $response_timeout set response timeout in seconds
2706
     * @param  array  $cookies          cookies to send
2707
     * @return string  data
2708
     * @deprecated
2709
     */
2710
    public function sendHTTPS($data, $timeout, $response_timeout, $cookies)
2711
    {
2712
        return $this->send($data, $timeout, $response_timeout, $cookies);
2713
    }
2714
2715
    /**
2716
     * if authenticating, set user credentials here
2717
     *
2718
     * @param string $username
2719
     * @param string $password
2720
     * @param string $authtype      (basic|digest|certificate|ntlm)
2721
     * @param array  $digestRequest (keys must be nonce, nc, realm, qop)
2722
     * @param array  $certRequest   (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2723
     */
2724
    public function setCredentials(
2725
        $username,
2726
        $password,
2727
        $authtype = 'basic',
2728
        $digestRequest = [],
2729
        $certRequest = [])
2730
    {
2731
        $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2732
        $this->appendDebug($this->varDump($digestRequest));
2733
        $this->debug('certRequest=');
2734
        $this->appendDebug($this->varDump($certRequest));
2735
        // cf. RFC 2617
2736
        if ('basic' === $authtype) {
2737
            $this->setHeader('Authorization', 'Basic ' . base64_encode(str_replace(':', '', $username) . ':' . $password));
2738
        } elseif ('digest' === $authtype) {
2739
            if (isset($digestRequest['nonce'])) {
2740
                $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2741
2742
                // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2743
2744
                // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2745
                $A1 = $username . ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2746
2747
                // H(A1) = MD5(A1)
2748
                $HA1 = md5($A1);
2749
2750
                // A2 = Method ":" digest-uri-value
2751
                $A2 = $this->request_method . ':' . $this->digest_uri;
2752
2753
                // H(A2)
2754
                $HA2 = md5($A2);
2755
2756
                // KD(secret, data) = H(concat(secret, ":", data))
2757
                // if qop == auth:
2758
                // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2759
                //                              ":" nc-value
2760
                //                              ":" unq(cnonce-value)
2761
                //                              ":" unq(qop-value)
2762
                //                              ":" H(A2)
2763
                //                            ) <">
2764
                // if qop is missing,
2765
                // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2766
2767
                $unhashedDigest = '';
2768
                $nonce          = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2769
                $cnonce         = $nonce;
2770
                if ('' !== $digestRequest['qop']) {
2771
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf('%08d', $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2772
                } else {
2773
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2774
                }
2775
2776
                $hashedDigest = md5($unhashedDigest);
2777
2778
                $opaque = '';
2779
                if (isset($digestRequest['opaque'])) {
2780
                    $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2781
                }
2782
2783
                $this->setHeader('Authorization', 'Digest username="'
2784
                                                  . $username
2785
                                                  . '", realm="'
2786
                                                  . $digestRequest['realm']
2787
                                                  . '", nonce="'
2788
                                                  . $nonce
2789
                                                  . '", uri="'
2790
                                                  . $this->digest_uri
2791
                                                  . $opaque
2792
                                                  . '", cnonce="'
2793
                                                  . $cnonce
2794
                                                  . '", nc='
2795
                                                  . sprintf('%08x', $digestRequest['nc'])
2796
                                                  . ', qop="'
2797
                                                  . $digestRequest['qop']
2798
                                                  . '", response="'
2799
                                                  . $hashedDigest
2800
                                                  . '"');
2801
            }
2802
        } elseif ('certificate' === $authtype) {
2803
            $this->certRequest = $certRequest;
2804
            $this->debug('Authorization header not set for certificate');
2805
        } elseif ('ntlm' === $authtype) {
2806
            // do nothing
2807
            $this->debug('Authorization header not set for ntlm');
2808
        }
2809
        $this->username      = $username;
2810
        $this->password      = $password;
2811
        $this->authtype      = $authtype;
2812
        $this->digestRequest = $digestRequest;
2813
    }
2814
2815
    /**
2816
     * set the soapaction value
2817
     *
2818
     * @param string $soapaction
2819
     */
2820
    public function setSOAPAction($soapaction)
2821
    {
2822
        $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2823
    }
2824
2825
    /**
2826
     * use http encoding
2827
     *
2828
     * @param string $enc encoding style. supported values: gzip, deflate, or both
2829
     */
2830
    public function setEncoding($enc = 'gzip, deflate')
2831
    {
2832
        if (function_exists('gzdeflate')) {
2833
            $this->protocol_version = '1.1';
2834
            $this->setHeader('Accept-Encoding', $enc);
2835
            if (!isset($this->outgoing_headers['Connection'])) {
2836
                $this->setHeader('Connection', 'close');
2837
                $this->persistentConnection = false;
2838
            }
2839
            // deprecated as of PHP 5.3.0
2840
            //set_magic_quotes_runtime(0);
2841
            $this->encoding = $enc;
2842
        }
2843
    }
2844
2845
    /**
2846
     * set proxy info here
2847
     *
2848
     * @param    bool|string $proxyhost     use an empty string to remove proxy
2849
     * @param    bool|string $proxyport
2850
     * @param    bool|string $proxyusername
2851
     * @param    bool|string $proxypassword
2852
     * @param    bool|string $proxyauthtype (basic|ntlm)
2853
     */
2854
    public function setProxy(
2855
        $proxyhost,
2856
        $proxyport,
2857
        $proxyusername = '',
2858
        $proxypassword = '',
2859
        $proxyauthtype = 'basic')
2860
    {
2861
        if ($proxyhost) {
2862
            $this->proxy = [
2863
                'host'     => $proxyhost,
2864
                'port'     => $proxyport,
2865
                'username' => $proxyusername,
2866
                'password' => $proxypassword,
2867
                'authtype' => $proxyauthtype,
2868
            ];
2869
            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...
2870
                $this->setHeader('Proxy-Authorization', ' Basic ' . base64_encode($proxyusername . ':' . $proxypassword));
2871
            }
2872
        } else {
2873
            $this->debug('remove proxy');
2874
            $proxy = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $proxy is dead and can be removed.
Loading history...
2875
            $this->unsetHeader('Proxy-Authorization');
2876
        }
2877
    }
2878
2879
    /**
2880
     * Test if the given string starts with a header that is to be skipped.
2881
     * Skippable headers result from chunked transfer and proxy requests.
2882
     *
2883
     * @param string $data The string to check.
2884
     * @returns boolean Whether a skippable header was found.
2885
     * @return bool
2886
     */
2887
    private function isSkippableCurlHeader(&$data)
2888
    {
2889
        $skipHeaders = [
2890
            'HTTP/1.1 100',
2891
            'HTTP/1.0 301',
2892
            'HTTP/1.1 301',
2893
            'HTTP/1.0 302',
2894
            'HTTP/1.1 302',
2895
            'HTTP/1.0 401',
2896
            'HTTP/1.1 401',
2897
            'HTTP/1.0 200 Connection established',
2898
        ];
2899
        foreach ($skipHeaders as $hd) {
2900
            $prefix = mb_substr($data, 0, mb_strlen($hd));
2901
            if ($prefix == $hd) {
2902
                return true;
2903
            }
2904
        }
2905
2906
        return false;
2907
    }
2908
2909
    /**
2910
     * decode a string that is encoded w/ "chunked' transfer encoding
2911
     * as defined in RFC2068 19.4.6
2912
     *
2913
     * @param string $buffer
2914
     * @param string $lb
2915
     * @returns  string
2916
     * @deprecated
2917
     * @return string
2918
     */
2919
    public function decodeChunked($buffer, $lb)
2920
    {
2921
        // length := 0
2922
        $length = 0;
2923
        $new    = '';
2924
2925
        // read chunk-size, chunk-extension (if any) and CRLF
2926
        // get the position of the linebreak
2927
        $chunkend = mb_strpos($buffer, $lb);
2928
        if (false === $chunkend) {
2929
            $this->debug('no linebreak found in decodeChunked');
2930
2931
            return $new;
2932
        }
2933
        $temp       = mb_substr($buffer, 0, $chunkend);
2934
        $chunk_size = hexdec(trim($temp));
2935
        $chunkstart = $chunkend + mb_strlen($lb);
2936
        // while (chunk-size > 0) {
2937
        while ($chunk_size > 0) {
2938
            $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2939
            $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

2939
            $chunkend = mb_strpos($buffer, $lb, /** @scrutinizer ignore-type */ $chunkstart + $chunk_size);
Loading history...
2940
2941
            // Just in case we got a broken connection
2942
            if (false === $chunkend) {
2943
                $chunk = mb_substr($buffer, $chunkstart);
2944
                // append chunk-data to entity-body
2945
                $new    .= $chunk;
2946
                $length += mb_strlen($chunk);
2947
                break;
2948
            }
2949
2950
            // read chunk-data and CRLF
2951
            $chunk = mb_substr($buffer, $chunkstart, $chunkend - $chunkstart);
2952
            // append chunk-data to entity-body
2953
            $new .= $chunk;
2954
            // length := length + chunk-size
2955
            $length += mb_strlen($chunk);
2956
            // read chunk-size and CRLF
2957
            $chunkstart = $chunkend + mb_strlen($lb);
2958
2959
            $chunkend = mb_strpos($buffer, $lb, $chunkstart) + mb_strlen($lb);
2960
            if (false === $chunkend) {
2961
                break; //Just in case we got a broken connection
2962
            }
2963
            $temp       = mb_substr($buffer, $chunkstart, $chunkend - $chunkstart);
2964
            $chunk_size = hexdec(trim($temp));
2965
            $chunkstart = $chunkend;
2966
        }
2967
2968
        return $new;
2969
    }
2970
2971
    /**
2972
     * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2973
     *
2974
     * @param  string $data       HTTP body
2975
     * @param  string $cookie_str data for HTTP Cookie header
2976
     */
2977
    private function buildPayload($data, $cookie_str = '')
2978
    {
2979
        // Note: for cURL connections, $this->outgoing_payload is ignored,
2980
        // as is the Content-Length header, but these are still created as
2981
        // debugging guides.
2982
2983
        // add content-length header
2984
        if ('GET' !== $this->request_method) {
2985
            $this->setHeader('Content-Length', mb_strlen($data));
2986
        }
2987
2988
        // start building outgoing payload:
2989
        if ($this->proxy) {
2990
            $uri = $this->url;
2991
        } else {
2992
            $uri = $this->uri;
2993
        }
2994
        $req = "$this->request_method $uri HTTP/$this->protocol_version";
2995
        $this->debug("HTTP request: $req");
2996
        $this->outgoing_payload = "$req\r\n";
2997
2998
        // loop thru headers, serializing
2999
        foreach ($this->outgoing_headers as $k => $v) {
3000
            $hdr = $k . ': ' . $v;
3001
            $this->debug("HTTP header: $hdr");
3002
            $this->outgoing_payload .= "$hdr\r\n";
3003
        }
3004
3005
        // add any cookies
3006
        if ('' !== $cookie_str) {
3007
            $hdr = 'Cookie: ' . $cookie_str;
3008
            $this->debug("HTTP header: $hdr");
3009
            $this->outgoing_payload .= "$hdr\r\n";
3010
        }
3011
3012
        // header/body separator
3013
        $this->outgoing_payload .= "\r\n";
3014
3015
        // add data
3016
        $this->outgoing_payload .= $data;
3017
    }
3018
3019
    /**
3020
     * sends the SOAP request via HTTP[S]
3021
     *
3022
     * @param  string $data    message data
3023
     * @param  array  $cookies cookies to send
3024
     * @return bool true if OK, false if problem
3025
     */
3026
    private function sendRequest($data, $cookies = null)
3027
    {
3028
        // build cookie string
3029
        $cookie_str = $this->getCookiesForRequest($cookies, ('ssl' === $this->scheme) || ('https' === $this->scheme));
3030
3031
        // build payload
3032
        $this->buildPayload($data, $cookie_str);
3033
3034
        if ('socket' === $this->io_method()) {
3035
            // send payload
3036
            if (!fwrite($this->fp, $this->outgoing_payload, mb_strlen($this->outgoing_payload))) {
3037
                $this->setError('couldn\'t write message data to socket');
3038
                $this->debug('couldn\'t write message data to socket');
3039
3040
                return false;
3041
            }
3042
            $this->debug('wrote data to socket, length = ' . mb_strlen($this->outgoing_payload));
3043
3044
            return true;
3045
        }
3046
3047
        if ('curl' === $this->io_method()) {
3048
            // set payload
3049
            // cURL does say this should only be the verb, and in fact it
3050
            // turns out that the URI and HTTP version are appended to this, which
3051
            // some servers refuse to work with (so we no longer use this method!)
3052
            //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
3053
            $curl_headers = [];
3054
            foreach ($this->outgoing_headers as $k => $v) {
3055
                if ('Connection' === $k || 'Content-Length' === $k || 'Host' === $k || 'Authorization' === $k || 'Proxy-Authorization' === $k) {
3056
                    $this->debug("Skip cURL header $k: $v");
3057
                } else {
3058
                    $curl_headers[] = "$k: $v";
3059
                }
3060
            }
3061
            if ('' !== $cookie_str) {
3062
                $curl_headers[] = 'Cookie: ' . $cookie_str;
3063
            }
3064
            $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
3065
            $this->debug('set cURL HTTP headers');
3066
            if ('POST' === $this->request_method) {
3067
                $this->setCurlOption(CURLOPT_POST, 1);
3068
                $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
3069
                $this->debug('set cURL POST data');
3070
            }
3071
3072
            // insert custom user-set cURL options
3073
            foreach ($this->ch_options as $key => $val) {
3074
                $this->setCurlOption($key, $val);
3075
            }
3076
3077
            $this->debug('set cURL payload');
3078
3079
            return true;
3080
        }
3081
    }
3082
3083
    /**
3084
     * gets the SOAP response via HTTP[S]
3085
     *
3086
     * @return string the response (also sets member variables like incoming_payload)
3087
     */
3088
    private function getResponse()
3089
    {
3090
        $this->incoming_payload = '';
3091
3092
        if ('socket' === $this->io_method()) {
3093
            // loop until headers have been retrieved
3094
            $data = '';
3095
            while (!isset($lb)) {
3096
                // We might EOF during header read.
3097
                if (feof($this->fp)) {
3098
                    $this->incoming_payload = $data;
3099
                    $this->debug('found no headers before EOF after length ' . mb_strlen($data));
3100
                    $this->debug("received before EOF:\n" . $data);
3101
                    $this->setError('server failed to send headers');
3102
3103
                    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...
3104
                }
3105
3106
                $tmp    = fgets($this->fp, 256);
3107
                $tmplen = mb_strlen($tmp);
3108
                $this->debug("read line of $tmplen bytes: " . trim($tmp));
3109
3110
                if (0 == $tmplen) {
3111
                    $this->incoming_payload = $data;
3112
                    $this->debug('socket read of headers timed out after length ' . mb_strlen($data));
3113
                    $this->debug('read before timeout: ' . $data);
3114
                    $this->setError('socket read of headers timed out');
3115
3116
                    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...
3117
                }
3118
3119
                $data .= $tmp;
3120
                $pos  = mb_strpos($data, "\r\n\r\n");
3121
                if ($pos > 1) {
3122
                    $lb = "\r\n";
3123
                } else {
3124
                    $pos = mb_strpos($data, "\n\n");
3125
                    if ($pos > 1) {
3126
                        $lb = "\n";
3127
                    }
3128
                }
3129
                // remove 100 headers
3130
                if (isset($lb) && preg_match('/^HTTP\/1.1 100/', $data)) {
3131
                    unset($lb);
3132
                    $data = '';
3133
                }
3134
            }
3135
            // store header data
3136
            $this->incoming_payload .= $data;
3137
            $this->debug('found end of headers after length ' . mb_strlen($data));
3138
            // process headers
3139
            $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...
3140
            $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...
3141
            $this->incoming_headers = [];
3142
            $this->incoming_cookies = [];
3143
            foreach ($header_array as $header_line) {
3144
                $arr = explode(':', $header_line, 2);
3145
                if (is_array($arr) && count($arr) > 1) {
3146
                    $header_name                          = mb_strtolower(trim($arr[0]));
3147
                    $this->incoming_headers[$header_name] = trim($arr[1]);
3148
                    if ('set-cookie' === $header_name) {
3149
                        // TODO: allow multiple cookies from parseCookie
3150
                        $cookie = $this->parseCookie(trim($arr[1]));
3151
                        if ($cookie) {
3152
                            $this->incoming_cookies[] = $cookie;
3153
                            $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3154
                        } else {
3155
                            $this->debug('did not find cookie in ' . trim($arr[1]));
3156
                        }
3157
                    }
3158
                } elseif (isset($header_name)) {
3159
                    // append continuation line to previous header
3160
                    $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3161
                }
3162
            }
3163
3164
            // loop until msg has been received
3165
            if (isset($this->incoming_headers['transfer-encoding']) && 'chunked' === mb_strtolower($this->incoming_headers['transfer-encoding'])) {
3166
                $content_length = 2147483647;    // ignore any content-length header
3167
                $chunked        = true;
3168
                $this->debug('want to read chunked content');
3169
            } elseif (isset($this->incoming_headers['content-length'])) {
3170
                $content_length = $this->incoming_headers['content-length'];
3171
                $chunked        = false;
3172
                $this->debug("want to read content of length $content_length");
3173
            } else {
3174
                $content_length = 2147483647;
3175
                $chunked        = false;
3176
                $this->debug('want to read content to EOF');
3177
            }
3178
            $data = '';
3179
            do {
3180
                if ($chunked) {
3181
                    $tmp    = fgets($this->fp, 256);
3182
                    $tmplen = mb_strlen($tmp);
3183
                    $this->debug("read chunk line of $tmplen bytes");
3184
                    if (0 == $tmplen) {
3185
                        $this->incoming_payload = $data;
3186
                        $this->debug('socket read of chunk length timed out after length ' . mb_strlen($data));
3187
                        $this->debug("read before timeout:\n" . $data);
3188
                        $this->setError('socket read of chunk length timed out');
3189
3190
                        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...
3191
                    }
3192
                    $content_length = hexdec(trim($tmp));
3193
                    $this->debug("chunk length $content_length");
3194
                }
3195
                $strlen = 0;
3196
                while (($strlen < $content_length) && (!feof($this->fp))) {
3197
                    $readlen = min(8192, $content_length - $strlen);
3198
                    $tmp     = fread($this->fp, $readlen);
3199
                    $tmplen  = mb_strlen($tmp);
3200
                    $this->debug("read buffer of $tmplen bytes");
3201
                    if ((0 == $tmplen) && (!feof($this->fp))) {
3202
                        $this->incoming_payload = $data;
3203
                        $this->debug('socket read of body timed out after length ' . mb_strlen($data));
3204
                        $this->debug("read before timeout:\n" . $data);
3205
                        $this->setError('socket read of body timed out');
3206
3207
                        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...
3208
                    }
3209
                    $strlen += $tmplen;
3210
                    $data   .= $tmp;
3211
                }
3212
                if ($chunked && ($content_length > 0)) {
3213
                    $tmp    = fgets($this->fp, 256);
3214
                    $tmplen = mb_strlen($tmp);
3215
                    $this->debug("read chunk terminator of $tmplen bytes");
3216
                    if (0 == $tmplen) {
3217
                        $this->incoming_payload = $data;
3218
                        $this->debug('socket read of chunk terminator timed out after length ' . mb_strlen($data));
3219
                        $this->debug("read before timeout:\n" . $data);
3220
                        $this->setError('socket read of chunk terminator timed out');
3221
3222
                        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...
3223
                    }
3224
                }
3225
            } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3226
            if (feof($this->fp)) {
3227
                $this->debug('read to EOF');
3228
            }
3229
            $this->debug('read body of length ' . mb_strlen($data));
3230
            $this->incoming_payload .= $data;
3231
            $this->debug('received a total of ' . mb_strlen($this->incoming_payload) . ' bytes of data from server');
3232
3233
            // close filepointer
3234
            if ((isset($this->incoming_headers['connection']) && 'close' === mb_strtolower($this->incoming_headers['connection'])) || (!$this->persistentConnection) || feof($this->fp)) {
3235
                fclose($this->fp);
3236
                $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...
3237
                $this->debug('closed socket');
3238
            }
3239
3240
            // connection was closed unexpectedly
3241
            if ('' === $this->incoming_payload) {
3242
                $this->setError('no response from server');
3243
3244
                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...
3245
            }
3246
3247
            // decode transfer-encoding
3248
            //      if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
3249
            //          if (!$data = $this->decodeChunked($data, $lb)) {
3250
            //              $this->setError('Decoding of chunked data failed');
3251
            //              return false;
3252
            //          }
3253
            //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3254
            // set decoded payload
3255
            //          $this->incoming_payload = $header_data.$lb.$lb.$data;
3256
            //      }
3257
        } elseif ('curl' === $this->io_method()) {
3258
            // send and receive
3259
            $this->debug('send and receive with cURL');
3260
            $this->incoming_payload = curl_exec($this->ch);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type CurlHandle|resource expected by parameter $handle 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

3260
            $this->incoming_payload = curl_exec(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3261
            $data                   = $this->incoming_payload;
3262
3263
            $cErr = curl_error($this->ch);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type CurlHandle|resource expected by parameter $handle 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

3263
            $cErr = curl_error(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3264
            if ('' !== $cErr) {
3265
                $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 CurlHandle|resource expected by parameter $handle 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

3265
                $err = 'cURL ERROR: ' . curl_errno(/** @scrutinizer ignore-type */ $this->ch) . ': ' . $cErr . '<br>';
Loading history...
3266
                // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3267
                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 CurlHandle|resource expected by parameter $handle 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

3267
                foreach (curl_getinfo(/** @scrutinizer ignore-type */ $this->ch) as $k => $v) {
Loading history...
3268
                    $err .= "$k: $v<br>";
3269
                }
3270
                $this->debug($err);
3271
                $this->setError($err);
3272
                curl_close($this->ch);
0 ignored issues
show
Bug introduced by
$this->ch of type boolean is incompatible with the type CurlHandle|resource expected by parameter $handle 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

3272
                curl_close(/** @scrutinizer ignore-type */ $this->ch);
Loading history...
3273
3274
                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...
3275
            }
3276
3277
            //echo '<pre>';
3278
            //var_dump(curl_getinfo($this->ch));
3279
            //echo '</pre>';
3280
            // close curl
3281
            $this->debug('No cURL error, closing cURL');
3282
            curl_close($this->ch);
3283
3284
            // try removing skippable headers
3285
            $savedata = $data;
3286
            while ($this->isSkippableCurlHeader($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type true; however, parameter $data of Soap_transport_http::isSkippableCurlHeader() 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

3286
            while ($this->isSkippableCurlHeader(/** @scrutinizer ignore-type */ $data)) {
Loading history...
3287
                $this->debug('Found HTTP header to skip');
3288
                if (false !== ($pos = mb_strpos($data, "\r\n\r\n"))) {
3289
                    $data = ltrim(mb_substr($data, $pos));
3290
                } elseif (false !== ($pos = mb_strpos($data, "\n\n"))) {
3291
                    $data = ltrim(mb_substr($data, $pos));
3292
                }
3293
            }
3294
3295
            if ('' === $data) {
3296
                // have nothing left; just remove 100 header(s)
3297
                $data = $savedata;
3298
                while (preg_match('/^HTTP\/1.1 100/', $data)) {
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type true; however, parameter $subject of preg_match() 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

3298
                while (preg_match('/^HTTP\/1.1 100/', /** @scrutinizer ignore-type */ $data)) {
Loading history...
3299
                    if (false !== ($pos = mb_strpos($data, "\r\n\r\n"))) {
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type true; however, parameter $haystack of mb_strpos() 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

3299
                    if (false !== ($pos = mb_strpos(/** @scrutinizer ignore-type */ $data, "\r\n\r\n"))) {
Loading history...
3300
                        $data = ltrim(mb_substr($data, $pos));
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type true; however, parameter $string of mb_substr() 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

3300
                        $data = ltrim(mb_substr(/** @scrutinizer ignore-type */ $data, $pos));
Loading history...
3301
                    } elseif (false !== ($pos = mb_strpos($data, "\n\n"))) {
3302
                        $data = ltrim(mb_substr($data, $pos));
3303
                    }
3304
                }
3305
            }
3306
3307
            // separate content from HTTP headers
3308
            if (false !== ($pos = mb_strpos($data, "\r\n\r\n"))) {
3309
                $lb = "\r\n";
3310
            } elseif (false !== ($pos = mb_strpos($data, "\n\n"))) {
3311
                $lb = "\n";
3312
            } else {
3313
                $this->debug('no proper separation of headers and document');
3314
                $this->setError('no proper separation of headers and document');
3315
3316
                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...
3317
            }
3318
            $header_data  = trim(mb_substr($data, 0, $pos));
3319
            $header_array = explode($lb, $header_data);
3320
            $data         = ltrim(mb_substr($data, $pos));
3321
            $this->debug('found proper separation of headers and document');
3322
            $this->debug('cleaned data, stringlen: ' . mb_strlen($data));
3323
            // clean headers
3324
            foreach ($header_array as $header_line) {
3325
                $arr = explode(':', $header_line, 2);
3326
                if (is_array($arr) && count($arr) > 1) {
3327
                    $header_name                          = mb_strtolower(trim($arr[0]));
3328
                    $this->incoming_headers[$header_name] = trim($arr[1]);
3329
                    if ('set-cookie' === $header_name) {
3330
                        // TODO: allow multiple cookies from parseCookie
3331
                        $cookie = $this->parseCookie(trim($arr[1]));
3332
                        if ($cookie) {
3333
                            $this->incoming_cookies[] = $cookie;
3334
                            $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3335
                        } else {
3336
                            $this->debug('did not find cookie in ' . trim($arr[1]));
3337
                        }
3338
                    }
3339
                } elseif (isset($header_name)) {
3340
                    // append continuation line to previous header
3341
                    $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3342
                }
3343
            }
3344
        }
3345
3346
        $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...
3347
        $arr                        = explode(' ', $this->response_status_line, 3);
3348
        $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...
3349
        $http_status                = (int)$arr[1];
3350
        $http_reason                = count($arr) > 2 ? $arr[2] : '';
3351
3352
        // see if we need to resend the request with http digest authentication
3353
        if (isset($this->incoming_headers['location']) && (301 == $http_status || 302 == $http_status)) {
3354
            $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3355
            $this->setURL($this->incoming_headers['location']);
3356
            $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...
3357
3358
            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...
3359
        }
3360
3361
        // see if we need to resend the request with http digest authentication
3362
        if (isset($this->incoming_headers['www-authenticate']) && 401 == $http_status) {
3363
            $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3364
            if (false !== mb_strpos($this->incoming_headers['www-authenticate'], 'Digest ')) {
3365
                $this->debug('Server wants digest authentication');
3366
                // remove "Digest " from our elements
3367
                $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3368
3369
                // parse elements into array
3370
                $digestElements = explode(',', $digestString);
3371
                foreach ($digestElements as $val) {
3372
                    $tempElement                    = explode('=', trim($val), 2);
3373
                    $digestRequest[$tempElement[0]] = str_replace('"', '', $tempElement[1]);
3374
                }
3375
3376
                // should have (at least) qop, realm, nonce
3377
                if (isset($digestRequest['nonce'])) {
3378
                    $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 3371. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
3379
                    $this->tryagain = true;
3380
3381
                    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...
3382
                }
3383
            }
3384
            $this->debug('HTTP authentication failed');
3385
            $this->setError('HTTP authentication failed');
3386
3387
            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...
3388
        }
3389
3390
        if (($http_status >= 300 && $http_status <= 307) || ($http_status >= 400 && $http_status <= 417) || ($http_status >= 501 && $http_status <= 505)) {
3391
            $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3392
3393
            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...
3394
        }
3395
3396
        // decode content-encoding
3397
        if (isset($this->incoming_headers['content-encoding']) && '' !== $this->incoming_headers['content-encoding']) {
3398
            if ('deflate' === mb_strtolower($this->incoming_headers['content-encoding']) || 'gzip' === mb_strtolower($this->incoming_headers['content-encoding'])) {
3399
                // if decoding works, use it. else assume data wasn't gzencoded
3400
                if (function_exists('gzinflate')) {
3401
                    //$timer->setMarker('starting decoding of gzip/deflated content');
3402
                    // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3403
                    // this means there are no Zlib headers, although there should be
3404
                    $this->debug('The gzinflate function exists');
3405
                    $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...
3406
                    if ('deflate' === $this->incoming_headers['content-encoding']) {
3407
                        if (false !== ($degzdata = @gzinflate($data))) {
3408
                            $data = $degzdata;
3409
                            $this->debug('The payload has been inflated to ' . mb_strlen($data) . ' bytes');
3410
                            if (mb_strlen($data) < $datalen) {
3411
                                // test for the case that the payload has been compressed twice
3412
                                $this->debug('The inflated payload is smaller than the gzipped one; try again');
3413
                                if (false !== ($degzdata = @gzinflate($data))) {
3414
                                    $data = $degzdata;
3415
                                    $this->debug('The payload has been inflated again to ' . mb_strlen($data) . ' bytes');
3416
                                }
3417
                            }
3418
                        } else {
3419
                            $this->debug('Error using gzinflate to inflate the payload');
3420
                            $this->setError('Error using gzinflate to inflate the payload');
3421
                        }
3422
                    } elseif ('gzip' === $this->incoming_headers['content-encoding']) {
3423
                        if (false !== ($degzdata = @gzinflate(mb_substr($data, 10)))) {
3424
                            // do our best
3425
                            $data = $degzdata;
3426
                            $this->debug('The payload has been un-gzipped to ' . mb_strlen($data) . ' bytes');
3427
                            if (mb_strlen($data) < $datalen) {
3428
                                // test for the case that the payload has been compressed twice
3429
                                $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3430
                                if (false !== ($degzdata = @gzinflate(mb_substr($data, 10)))) {
3431
                                    $data = $degzdata;
3432
                                    $this->debug('The payload has been un-gzipped again to ' . mb_strlen($data) . ' bytes');
3433
                                }
3434
                            }
3435
                        } else {
3436
                            $this->debug('Error using gzinflate to un-gzip the payload');
3437
                            $this->setError('Error using gzinflate to un-gzip the payload');
3438
                        }
3439
                    }
3440
                    //$timer->setMarker('finished decoding of gzip/deflated content');
3441
                    //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3442
                    // set decoded payload
3443
                    $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...
3444
                } else {
3445
                    $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3446
                    $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3447
                }
3448
            } else {
3449
                $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3450
                $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3451
            }
3452
        } else {
3453
            $this->debug('No Content-Encoding header');
3454
        }
3455
3456
        if ('' === $data) {
3457
            $this->debug('no data after headers!');
3458
            $this->setError('no data present after HTTP headers');
3459
3460
            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...
3461
        }
3462
3463
        return $data;
3464
    }
3465
3466
    /**
3467
     * sets the content-type for the SOAP message to be sent
3468
     *
3469
     * @param string $type    the content type, MIME style
3470
     * @param mixed  $charset character set used for encoding (or false)
3471
     */
3472
    public function setContentType($type, $charset = false)
3473
    {
3474
        $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3475
    }
3476
3477
    /**
3478
     * specifies that an HTTP persistent connection should be used
3479
     *
3480
     * @return bool whether the request was honored by this method.
3481
     */
3482
    public function usePersistentConnection()
3483
    {
3484
        if (isset($this->outgoing_headers['Accept-Encoding'])) {
3485
            return false;
3486
        }
3487
        $this->protocol_version     = '1.1';
3488
        $this->persistentConnection = true;
3489
        $this->setHeader('Connection', 'Keep-Alive');
3490
3491
        return true;
3492
    }
3493
3494
    /**
3495
     * parse an incoming Cookie into it's parts
3496
     *
3497
     * @param  string $cookie_str content of cookie
3498
     * @return    bool|array with data of that cookie
3499
     */
3500
    /*
3501
     * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3502
     */
3503
    private function parseCookie($cookie_str)
3504
    {
3505
        $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3506
        $data       = preg_split('/;/', $cookie_str);
3507
        $value_str  = $data[0];
3508
3509
        $cookie_param = 'domain=';
3510
        $start        = mb_strpos($cookie_str, $cookie_param);
3511
        if ($start > 0) {
3512
            $domain = mb_substr($cookie_str, $start + mb_strlen($cookie_param));
3513
            $domain = mb_substr($domain, 0, mb_strpos($domain, ';'));
3514
        } else {
3515
            $domain = '';
3516
        }
3517
3518
        $cookie_param = 'expires=';
3519
        $start        = mb_strpos($cookie_str, $cookie_param);
3520
        if ($start > 0) {
3521
            $expires = mb_substr($cookie_str, $start + mb_strlen($cookie_param));
3522
            $expires = mb_substr($expires, 0, mb_strpos($expires, ';'));
3523
        } else {
3524
            $expires = '';
3525
        }
3526
3527
        $cookie_param = 'path=';
3528
        $start        = mb_strpos($cookie_str, $cookie_param);
3529
        if ($start > 0) {
3530
            $path = mb_substr($cookie_str, $start + mb_strlen($cookie_param));
3531
            $path = mb_substr($path, 0, mb_strpos($path, ';'));
3532
        } else {
3533
            $path = '/';
3534
        }
3535
3536
        $cookie_param = ';secure;';
3537
        $secure       = false;
3538
        if (false !== mb_strpos($cookie_str, $cookie_param)) {
3539
            $secure = true;
3540
        }
3541
3542
        $sep_pos = mb_strpos($value_str, '=');
3543
3544
        if ($sep_pos) {
3545
            $name   = mb_substr($value_str, 0, $sep_pos);
3546
            $value  = mb_substr($value_str, $sep_pos + 1);
3547
            $cookie = [
3548
                'name'    => $name,
3549
                'value'   => $value,
3550
                'domain'  => $domain,
3551
                'path'    => $path,
3552
                'expires' => $expires,
3553
                'secure'  => $secure,
3554
            ];
3555
3556
            return $cookie;
3557
        }
3558
3559
        return false;
3560
    }
3561
3562
    /**
3563
     * sort out cookies for the current request
3564
     *
3565
     * @param    array|null $cookies array with all cookies
3566
     * @param  bool         $secure  is the send-content secure or not?
3567
     * @return string  for Cookie-HTTP-Header
3568
     */
3569
    private function getCookiesForRequest($cookies, $secure = false)
3570
    {
3571
        $cookie_str = '';
3572
        if ((null !== $cookies) && is_array($cookies)) {
3573
            foreach ($cookies as $cookie) {
3574
                if (!is_array($cookie)) {
3575
                    continue;
3576
                }
3577
                $this->debug('check cookie for validity: ' . $cookie['name'] . '=' . $cookie['value']);
3578
                if (isset($cookie['expires']) && !empty($cookie['expires'])) {
3579
                    if (strtotime($cookie['expires']) <= time()) {
3580
                        $this->debug('cookie has expired');
3581
                        continue;
3582
                    }
3583
                }
3584
                if (isset($cookie['domain']) && !empty($cookie['domain'])) {
3585
                    $domain = preg_quote($cookie['domain']);
3586
                    if (!preg_match("'.*$domain$'i", $this->host)) {
3587
                        $this->debug('cookie has different domain');
3588
                        continue;
3589
                    }
3590
                }
3591
                if (isset($cookie['path']) && !empty($cookie['path'])) {
3592
                    $path = preg_quote($cookie['path']);
3593
                    if (!preg_match("'^$path.*'i", $this->path)) {
3594
                        $this->debug('cookie is for a different path');
3595
                        continue;
3596
                    }
3597
                }
3598
                if ((!$secure) && isset($cookie['secure']) && $cookie['secure']) {
3599
                    $this->debug('cookie is secure, transport is not');
3600
                    continue;
3601
                }
3602
                $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3603
                $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3604
            }
3605
        }
3606
3607
        return $cookie_str;
3608
    }
3609
}
3610
3611
/**
3612
 * nusoap_server allows the user to create a SOAP server
3613
 * that is capable of receiving messages and returning responses
3614
 *
3615
 * @author   Dietrich Ayala <[email protected]>
3616
 * @author   Scott Nichol <[email protected]>
3617
 */
3618
class Nusoap_server extends Nusoap_base
3619
{
3620
    /**
3621
     * HTTP headers of request
3622
     *
3623
     * @var array
3624
     */
3625
    private $headers = [];
3626
    /**
3627
     * HTTP request
3628
     *
3629
     * @var string
3630
     */
3631
    private $request = '';
3632
    /**
3633
     * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3634
     *
3635
     * @var string
3636
     */
3637
    public $requestHeaders = '';
3638
    /**
3639
     * SOAP Headers from request (parsed)
3640
     *
3641
     * @var mixed
3642
     */
3643
    public $requestHeader;
3644
    /**
3645
     * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3646
     *
3647
     * @var string
3648
     */
3649
    public $document = '';
3650
    /**
3651
     * SOAP payload for request (text)
3652
     *
3653
     * @var string
3654
     */
3655
    public $requestSOAP = '';
3656
    /**
3657
     * requested method namespace URI
3658
     *
3659
     * @var string
3660
     */
3661
    private $methodURI = '';
3662
    /**
3663
     * name of method requested
3664
     *
3665
     * @var string
3666
     */
3667
    private $methodname = '';
3668
    /**
3669
     * method parameters from request
3670
     *
3671
     * @var array
3672
     */
3673
    private $methodparams = [];
3674
    /**
3675
     * SOAP Action from request
3676
     *
3677
     * @var string
3678
     */
3679
    private $SOAPAction = '';
3680
    /**
3681
     * character set encoding of incoming (request) messages
3682
     *
3683
     * @var string
3684
     */
3685
    public $xml_encoding = '';
3686
    /**
3687
     * toggles whether the parser decodes element content w/ utf8_decode()
3688
     *
3689
     * @var bool
3690
     */
3691
    public $decode_utf8 = true;
3692
3693
    /**
3694
     * HTTP headers of response
3695
     *
3696
     * @var array
3697
     */
3698
    public $outgoing_headers = [];
3699
    /**
3700
     * HTTP response
3701
     *
3702
     * @var string
3703
     */
3704
    private $response = '';
3705
    /**
3706
     * SOAP headers for response (text or array of soapval or associative array)
3707
     *
3708
     * @var mixed
3709
     */
3710
    public $responseHeaders = '';
3711
    /**
3712
     * SOAP payload for response (text)
3713
     *
3714
     * @var string
3715
     */
3716
    private $responseSOAP = '';
3717
    /**
3718
     * method return value to place in response
3719
     *
3720
     * @var mixed
3721
     */
3722
    private $methodreturn = false;
3723
    /**
3724
     * whether $methodreturn is a string of literal XML
3725
     *
3726
     * @var bool
3727
     */
3728
    public $methodreturnisliteralxml = false;
3729
    /**
3730
     * SOAP fault for response (or false)
3731
     *
3732
     * @var mixed
3733
     */
3734
    private $fault = false;
3735
    /**
3736
     * text indication of result (for debugging)
3737
     *
3738
     * @var string
3739
     */
3740
    private $result = 'successful';
3741
3742
    /**
3743
     * assoc array of operations => opData; operations are added by the register()
3744
     * method or by parsing an external WSDL definition
3745
     *
3746
     * @var array
3747
     */
3748
    private $operations = [];
3749
    /**
3750
     * wsdl instance (if one)
3751
     *
3752
     * @var mixed
3753
     */
3754
    private $wsdl = false;
3755
    /**
3756
     * URL for WSDL (if one)
3757
     *
3758
     * @var mixed
3759
     */
3760
    private $externalWSDLURL = false;
3761
    /**
3762
     * whether to append debug to response as XML comment
3763
     *
3764
     * @var bool
3765
     */
3766
    public $debug_flag = false;
3767
3768
    /**
3769
     * constructor
3770
     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3771
     *
3772
     * @param mixed $wsdl file path or URL (string) , or wsdl instance (object)
3773
     */
3774
    public function __construct($wsdl = false)
3775
    {
3776
        parent::__construct();
3777
        // turn on debugging?
3778
        global $debug;
3779
        global $HTTP_SERVER_VARS;
3780
3781
        if (isset($_SERVER)) {
3782
            $this->debug('_SERVER is defined:');
3783
            $this->appendDebug($this->varDump($_SERVER));
3784
        } elseif (isset($HTTP_SERVER_VARS)) {
3785
            $this->debug('HTTP_SERVER_VARS is defined:');
3786
            $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3787
        } else {
3788
            $this->debug('Neither _SERVER nor HTTP_SERVER_VARS is defined.');
3789
        }
3790
3791
        if (isset($debug)) {
3792
            $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3793
            $this->debug_flag = $debug;
3794
        } elseif (\Xmf\Request::hasVar('QUERY_STRING', 'SERVER')) {
3795
            $qs = explode('&', $_SERVER['QUERY_STRING']);
3796
            foreach ($qs as $v) {
3797
                if (0 === mb_strpos($v, 'debug=')) {
3798
                    $this->debug('In nusoap_server, set debug_flag=' . mb_substr($v, 6) . ' based on query string #1');
3799
                    $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...
3800
                }
3801
            }
3802
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3803
            $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3804
            foreach ($qs as $v) {
3805
                if (0 === mb_strpos($v, 'debug=')) {
3806
                    $this->debug('In nusoap_server, set debug_flag=' . mb_substr($v, 6) . ' based on query string #2');
3807
                    $this->debug_flag = mb_substr($v, 6);
3808
                }
3809
            }
3810
        }
3811
3812
        // wsdl
3813
        if ($wsdl) {
3814
            $this->debug('In nusoap_server, WSDL is specified');
3815
            if (is_object($wsdl) && ('Wsdl' === get_class($wsdl))) {
3816
                $this->wsdl            = $wsdl;
3817
                $this->externalWSDLURL = $this->wsdl->wsdl;
3818
                $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3819
            } else {
3820
                $this->debug('Create wsdl from ' . $wsdl);
3821
                $this->wsdl            = new wsdl($wsdl);
3822
                $this->externalWSDLURL = $wsdl;
3823
            }
3824
            $this->appendDebug($this->wsdl->getDebug());
3825
            $this->wsdl->clearDebug();
3826
            if (false !== ($err = $this->wsdl->getError())) {
3827
                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...
3828
            }
3829
        }
3830
    }
3831
3832
    /**
3833
     * processes request and returns response
3834
     *
3835
     * @param string $data usually is the value of $HTTP_RAW_POST_DATA
3836
     */
3837
    public function service($data)
3838
    {
3839
        global $HTTP_SERVER_VARS;
3840
3841
        $rm = '';
3842
        if (\Xmf\Request::hasVar('REQUEST_METHOD', 'SERVER')) {
3843
            $rm = $_SERVER['REQUEST_METHOD'];
3844
        } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
3845
            $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
3846
        }
3847
3848
        $qs = '';
3849
        if (\Xmf\Request::hasVar('QUERY_STRING', 'SERVER')) {
3850
            $qs = $_SERVER['QUERY_STRING'];
3851
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3852
            $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3853
        }
3854
        $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . mb_strlen($data));
3855
3856
        if ('POST' === $rm) {
3857
            $this->debug('In service, invoke the request');
3858
            $this->parse_request($data);
3859
            if (!$this->fault) {
3860
                $this->invoke_method();
3861
            }
3862
            if (!$this->fault) {
3863
                $this->serialize_return();
3864
            }
3865
            $this->send_response();
3866
        } elseif (preg_match('/wsdl/', $qs)) {
3867
            $this->debug('In service, this is a request for WSDL');
3868
            if ($this->externalWSDLURL) {
3869
                if (false !== mb_strpos($this->externalWSDLURL, 'http://')) {
3870
                    // assume URL
3871
                    $this->debug('In service, re-direct for WSDL');
3872
                    header('Location: ' . $this->externalWSDLURL);
3873
                } else {
3874
                    // assume file
3875
                    $this->debug('In service, use file passthru for WSDL');
3876
                    header("Content-Type: text/xml\r\n");
3877
                    $pos = mb_strpos($this->externalWSDLURL, 'file://');
3878
                    if (false === $pos) {
3879
                        $filename = $this->externalWSDLURL;
0 ignored issues
show
Unused Code introduced by
The assignment to $filename is dead and can be removed.
Loading history...
3880
                    } else {
3881
                        $filename = mb_substr($this->externalWSDLURL, $pos + 7);
3882
                    }
3883
                    $fp = fopen($this->externalWSDLURL, 'rb');
3884
                    fpassthru($fp);
3885
                }
3886
            } elseif ($this->wsdl) {
3887
                $this->debug('In service, serialize WSDL');
3888
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3889
                print $this->wsdl->serialize($this->debug_flag);
3890
                if ($this->debug_flag) {
3891
                    $this->debug('wsdl:');
3892
                    $this->appendDebug($this->varDump($this->wsdl));
3893
                    print $this->getDebugAsXMLComment();
3894
                }
3895
            } else {
3896
                $this->debug('In service, there is no WSDL');
3897
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3898
                print 'This service does not provide WSDL';
3899
            }
3900
        } elseif ($this->wsdl) {
3901
            $this->debug('In service, return Web description');
3902
            print $this->wsdl->webDescription();
3903
        } else {
3904
            $this->debug('In service, no Web description');
3905
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3906
            print 'This service does not provide a Web description';
3907
        }
3908
    }
3909
3910
    /**
3911
     * parses HTTP request headers.
3912
     *
3913
     * The following fields are set by this function (when successful)
3914
     *
3915
     * headers
3916
     * request
3917
     * xml_encoding
3918
     * SOAPAction
3919
     */
3920
    private function parse_http_headers()
3921
    {
3922
        global $HTTP_SERVER_VARS;
3923
3924
        $this->request    = '';
3925
        $this->SOAPAction = '';
3926
        if (function_exists('getallheaders')) {
3927
            $this->debug('In parse_http_headers, use getallheaders');
3928
            $headers = getallheaders();
3929
            foreach ($headers as $k => $v) {
3930
                $k                 = mb_strtolower($k);
3931
                $this->headers[$k] = $v;
3932
                $this->request     .= "$k: $v\r\n";
3933
                $this->debug("$k: $v");
3934
            }
3935
            // get SOAPAction header
3936
            if (isset($this->headers['soapaction'])) {
3937
                $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
3938
            }
3939
            // get the character encoding of the incoming request
3940
            if (isset($this->headers['content-type']) && mb_strpos($this->headers['content-type'], '=')) {
3941
                $enc                = str_replace('"', '', mb_substr(mb_strstr($this->headers['content-type'], '='), 1));
3942
                $this->xml_encoding = 'US-ASCII';
3943
                if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3944
                    $this->xml_encoding = mb_strtoupper($enc);
3945
                }
3946
            } else {
3947
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3948
                $this->xml_encoding = 'ISO-8859-1';
3949
            }
3950
        } elseif (isset($_SERVER) && is_array($_SERVER)) {
3951
            $this->debug('In parse_http_headers, use _SERVER');
3952
            foreach ($_SERVER as $k => $v) {
3953
                if (0 === mb_strpos($k, 'HTTP_')) {
3954
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', mb_substr($k, 5))));
3955
                } else {
3956
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', $k)));
3957
                }
3958
                if ('soapaction' === $k) {
3959
                    // get SOAPAction header
3960
                    $k                = 'SOAPAction';
3961
                    $v                = str_replace('"', '', $v);
3962
                    $v                = str_replace('\\', '', $v);
3963
                    $this->SOAPAction = $v;
3964
                } elseif ('content-type' === $k) {
3965
                    // get the character encoding of the incoming request
3966
                    if (mb_strpos($v, '=')) {
3967
                        $enc                = mb_substr(mb_strstr($v, '='), 1);
3968
                        $enc                = str_replace('"', '', $enc);
3969
                        $enc                = str_replace('\\', '', $enc);
3970
                        $this->xml_encoding = 'US-ASCII';
3971
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3972
                            $this->xml_encoding = mb_strtoupper($enc);
3973
                        }
3974
                    } else {
3975
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3976
                        $this->xml_encoding = 'ISO-8859-1';
3977
                    }
3978
                }
3979
                $this->headers[$k] = $v;
3980
                $this->request     .= "$k: $v\r\n";
3981
                $this->debug("$k: $v");
3982
            }
3983
        } elseif (is_array($HTTP_SERVER_VARS)) {
3984
            $this->debug('In parse_http_headers, use HTTP_SERVER_VARS');
3985
            foreach ($HTTP_SERVER_VARS as $k => $v) {
3986
                if (0 === mb_strpos($k, 'HTTP_')) {
3987
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', mb_substr($k, 5))));
3988
                    $k = mb_strtolower(mb_substr($k, 5));
3989
                } else {
3990
                    $k = str_replace(' ', '-', mb_strtolower(str_replace('_', ' ', $k)));
3991
                    $k = mb_strtolower($k);
3992
                }
3993
                if ('soapaction' === $k) {
3994
                    // get SOAPAction header
3995
                    $k                = 'SOAPAction';
3996
                    $v                = str_replace('"', '', $v);
3997
                    $v                = str_replace('\\', '', $v);
3998
                    $this->SOAPAction = $v;
3999
                } elseif ('content-type' === $k) {
4000
                    // get the character encoding of the incoming request
4001
                    if (mb_strpos($v, '=')) {
4002
                        $enc                = mb_substr(mb_strstr($v, '='), 1);
4003
                        $enc                = str_replace('"', '', $enc);
4004
                        $enc                = str_replace('\\', '', $enc);
4005
                        $this->xml_encoding = 'US-ASCII';
4006
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
4007
                            $this->xml_encoding = mb_strtoupper($enc);
4008
                        }
4009
                    } else {
4010
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4011
                        $this->xml_encoding = 'ISO-8859-1';
4012
                    }
4013
                }
4014
                $this->headers[$k] = $v;
4015
                $this->request     .= "$k: $v\r\n";
4016
                $this->debug("$k: $v");
4017
            }
4018
        } else {
4019
            $this->debug('In parse_http_headers, HTTP headers not accessible');
4020
            $this->setError('HTTP headers not accessible');
4021
        }
4022
    }
4023
4024
    /**
4025
     * parses a request
4026
     *
4027
     * The following fields are set by this function (when successful)
4028
     *
4029
     * headers
4030
     * request
4031
     * xml_encoding
4032
     * SOAPAction
4033
     * request
4034
     * requestSOAP
4035
     * methodURI
4036
     * methodname
4037
     * methodparams
4038
     * requestHeaders
4039
     * document
4040
     *
4041
     * This sets the fault field on error
4042
     *
4043
     * @param string $data XML string
4044
     */
4045
    private function parse_request($data = '')
4046
    {
4047
        $this->debug('entering parse_request()');
4048
        $this->parse_http_headers();
4049
        $this->debug('got character encoding: ' . $this->xml_encoding);
4050
        // uncompress if necessary
4051
        if (isset($this->headers['content-encoding']) && '' !== $this->headers['content-encoding']) {
4052
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
4053
            if ('deflate' === $this->headers['content-encoding'] || 'gzip' === $this->headers['content-encoding']) {
4054
                // if decoding works, use it. else assume data wasn't gzencoded
4055
                if (function_exists('gzuncompress')) {
4056
                    if ('deflate' === $this->headers['content-encoding'] && $degzdata = @gzuncompress($data)) {
4057
                        $data = $degzdata;
4058
                    } elseif ('gzip' === $this->headers['content-encoding'] && $degzdata = gzinflate(mb_substr($data, 10))) {
4059
                        $data = $degzdata;
4060
                    } else {
4061
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
4062
4063
                        return;
4064
                    }
4065
                } else {
4066
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
4067
4068
                    return;
4069
                }
4070
            }
4071
        }
4072
        $this->request     .= "\r\n" . $data;
4073
        $data              = $this->parseRequest($this->headers, $data);
4074
        $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...
4075
        $this->debug('leaving parse_request');
4076
    }
4077
4078
    /**
4079
     * invokes a PHP function for the requested SOAP method
4080
     *
4081
     * The following fields are set by this function (when successful)
4082
     *
4083
     * methodreturn
4084
     *
4085
     * Note that the PHP function that is called may also set the following
4086
     * fields to affect the response sent to the client
4087
     *
4088
     * responseHeaders
4089
     * outgoing_headers
4090
     *
4091
     * This sets the fault field on error
4092
     */
4093
    private function invoke_method()
4094
    {
4095
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
4096
4097
        //
4098
        // if you are debugging in this area of the code, your service uses a class to implement methods,
4099
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
4100
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
4101
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
4102
        // the XML request and reading the XML response.  you need to add the RequestElementName and
4103
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
4104
        // generates for the method.  these parameters are used to specify the correct XML element names
4105
        // for .NET to use, i.e. the names with the '.' in them.
4106
        //
4107
        $orig_methodname = $this->methodname;
4108
        if ($this->wsdl) {
4109
            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...
4110
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
4111
                $this->appendDebug('opData=' . $this->varDump($this->opData));
4112
            } elseif (false !== ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction))) {
4113
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
4114
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
4115
                $this->appendDebug('opData=' . $this->varDump($this->opData));
4116
                $this->methodname = $this->opData['name'];
4117
            } else {
4118
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
4119
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
4120
4121
                return;
4122
            }
4123
        } else {
4124
            $this->debug('in invoke_method, no WSDL to validate method');
4125
        }
4126
4127
        // if a . is present in $this->methodname, we see if there is a class in scope,
4128
        // which could be referred to. We will also distinguish between two deliminators,
4129
        // to allow methods to be called a the class or an instance
4130
        $delim = '';
4131
        if (mb_strpos($this->methodname, '..') > 0) {
4132
            $delim = '..';
4133
        } elseif (mb_strpos($this->methodname, '.') > 0) {
4134
            $delim = '.';
4135
        }
4136
        $this->debug("in invoke_method, delim=$delim");
4137
4138
        $class  = '';
4139
        $method = '';
4140
        if (mb_strlen($delim) > 0 && 1 == mb_substr_count($this->methodname, $delim)) {
4141
            $try_class = mb_substr($this->methodname, 0, mb_strpos($this->methodname, $delim));
4142
            if (class_exists($try_class)) {
4143
                // get the class and method name
4144
                $class  = $try_class;
4145
                $method = mb_substr($this->methodname, mb_strpos($this->methodname, $delim) + mb_strlen($delim));
4146
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4147
            } else {
4148
                $this->debug("in invoke_method, class=$try_class not found");
4149
            }
4150
        } elseif (mb_strlen($delim) > 0 && mb_substr_count($this->methodname, $delim) > 1) {
4151
            $split  = explode($delim, $this->methodname);
4152
            $method = array_pop($split);
4153
            $class  = implode('\\', $split);
4154
        } else {
4155
            $try_class = '';
4156
            $this->debug('in invoke_method, no class to try');
4157
        }
4158
4159
        // does method exist?
4160
        if ('' === $class) {
4161
            if (!function_exists($this->methodname)) {
4162
                $this->debug("in invoke_method, function '$this->methodname' not found!");
4163
                $this->result = 'fault: method not found';
4164
                $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...
4165
4166
                return;
4167
            }
4168
        } else {
4169
            $method_to_compare = (0 === mb_strpos(PHP_VERSION, '4.')) ? mb_strtolower($method) : $method;
4170
            if (!in_array($method_to_compare, get_class_methods($class))) {
4171
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4172
                $this->result = 'fault: method not found';
4173
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4174
4175
                return;
4176
            }
4177
        }
4178
4179
        // evaluate message, getting back parameters
4180
        // verify that request parameters match the method's signature
4181
        if (!$this->verify_method($this->methodname, $this->methodparams)) {
4182
            // debug
4183
            $this->debug('ERROR: request not verified against method signature');
4184
            $this->result = 'fault: request failed validation against method signature';
4185
            // return fault
4186
            $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
4187
4188
            return;
4189
        }
4190
4191
        // if there are parameters to pass
4192
        $this->debug('in invoke_method, params:');
4193
        $this->appendDebug($this->varDump($this->methodparams));
4194
        $this->debug("in invoke_method, calling '$this->methodname'");
4195
        if (!function_exists('call_user_func_array')) {
4196
            if ('' === $class) {
4197
                $this->debug('in invoke_method, calling function using eval()');
4198
                $funcCall = "\$this->methodreturn = $this->methodname(";
4199
            } else {
4200
                if ('..' === $delim) {
4201
                    $this->debug('in invoke_method, calling class method using eval()');
4202
                    $funcCall = '$this->methodreturn = ' . $class . '::' . $method . '(';
4203
                } else {
4204
                    $this->debug('in invoke_method, calling instance method using eval()');
4205
                    // generate unique instance name
4206
                    $instname = '$inst_' . time();
4207
                    $funcCall = $instname . ' = new ' . $class . '(); ';
4208
                    $funcCall .= '$this->methodreturn = ' . $instname . '->' . $method . '(';
4209
                }
4210
            }
4211
            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...
4212
                foreach ($this->methodparams as $param) {
4213
                    if (is_array($param) || is_object($param)) {
4214
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4215
4216
                        return;
4217
                    }
4218
                    $funcCall .= "\"$param\",";
4219
                }
4220
                $funcCall = mb_substr($funcCall, 0, -1);
4221
            }
4222
            $funcCall .= ');';
4223
            $this->debug('in invoke_method, function call: ' . $funcCall);
4224
            @eval($funcCall);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
4225
        } else {
4226
            if ('' === $class) {
4227
                $this->debug('in invoke_method, calling function using call_user_func_array()');
4228
                $call_arg = $this->methodname;    // straight assignment changes $this->methodname to lower case after call_user_func_array()
4229
            } elseif ('..' === $delim) {
4230
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
4231
                $call_arg = [$class, $method];
4232
            } else {
4233
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4234
                $instance = new $class();
4235
                $call_arg = [&$instance, $method];
4236
            }
4237
            if (is_array($this->methodparams)) {
0 ignored issues
show
introduced by
The condition is_array($this->methodparams) is always true.
Loading history...
4238
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4239
            } else {
4240
                $this->methodreturn = call_user_func_array($call_arg, []);
4241
            }
4242
        }
4243
        $this->debug('in invoke_method, methodreturn:');
4244
        $this->appendDebug($this->varDump($this->methodreturn));
4245
        $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
4246
    }
4247
4248
    /**
4249
     * serializes the return value from a PHP function into a full SOAP Envelope
4250
     *
4251
     * The following fields are set by this function (when successful)
4252
     *
4253
     * responseSOAP
4254
     *
4255
     * This sets the fault field on error
4256
     */
4257
    private function serialize_return()
4258
    {
4259
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4260
        // if fault
4261
        if (isset($this->methodreturn) && is_object($this->methodreturn) && (('Soap_fault' === get_class($this->methodreturn)) || ('Nusoap_fault' === get_class($this->methodreturn)))) {
4262
            $this->debug('got a fault object from method');
4263
            $this->fault = $this->methodreturn;
4264
4265
            return;
4266
        }
4267
4268
        if ($this->methodreturnisliteralxml) {
4269
            $return_val = $this->methodreturn;
4270
            // returned value(s)
4271
        } else {
4272
            $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
4273
            $this->debug('serializing return value');
4274
            if ($this->wsdl) {
4275
                if (is_array($this->opData['output']['parts']) && count($this->opData['output']['parts']) > 1) {
4276
                    $this->debug('more than one output part, so use the method return unchanged');
4277
                    $opParams = $this->methodreturn;
4278
                } elseif (1 == count($this->opData['output']['parts'])) {
4279
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
4280
                    // TODO: verify that it is not already wrapped!
4281
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
4282
                    //  $this->debug('wrap in element named ' . $name);
4283
                    //}
4284
                    $opParams = [$this->methodreturn];
4285
                }
4286
                $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...
4287
                $this->appendDebug($this->wsdl->getDebug());
4288
                $this->wsdl->clearDebug();
4289
                if (false !== ($errstr = $this->wsdl->getError())) {
4290
                    $this->debug('got wsdl error: ' . $errstr);
4291
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4292
4293
                    return;
4294
                }
4295
            } else {
4296
                if (isset($this->methodreturn)) {
4297
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
4298
                } else {
4299
                    $return_val = '';
4300
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
4301
                }
4302
            }
4303
        }
4304
        $this->debug('return value:');
4305
        $this->appendDebug($this->varDump($return_val));
4306
4307
        $this->debug('serializing response');
4308
        if ($this->wsdl) {
4309
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4310
            if ('rpc' === $this->opData['style']) {
4311
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4312
                if ('literal' === $this->opData['output']['use']) {
4313
                    // 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
4314
                    if ($this->methodURI) {
4315
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4316
                    } else {
4317
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4318
                    }
4319
                } else {
4320
                    if ($this->methodURI) {
4321
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4322
                    } else {
4323
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4324
                    }
4325
                }
4326
            } else {
4327
                $this->debug('style is not rpc for serialization: assume document');
4328
                $payload = $return_val;
4329
            }
4330
        } else {
4331
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4332
            $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4333
        }
4334
        $this->result = 'successful';
4335
        if ($this->wsdl) {
4336
            //if ($this->debug_flag) {
4337
            $this->appendDebug($this->wsdl->getDebug());
4338
            //  }
4339
            $encodingStyle = '';
4340
            if (isset($this->opData['output']['encodingStyle'])) {
4341
                $encodingStyle = $this->opData['output']['encodingStyle'];
4342
            }
4343
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4344
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
4345
        } else {
4346
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
4347
        }
4348
        $this->debug('Leaving serialize_return');
4349
    }
4350
4351
    /**
4352
     * sends an HTTP response
4353
     *
4354
     * The following fields are set by this function (when successful)
4355
     *
4356
     * outgoing_headers
4357
     * response
4358
     */
4359
    private function send_response()
4360
    {
4361
        $this->debug('Enter send_response');
4362
        if ($this->fault) {
4363
            $payload                  = $this->fault->serialize();
4364
            $this->outgoing_headers[] = 'HTTP/1.0 500 Internal Server Error';
4365
            $this->outgoing_headers[] = 'Status: 500 Internal Server Error';
4366
        } else {
4367
            $payload = $this->responseSOAP;
4368
            // Some combinations of PHP+Web server allow the Status
4369
            // to come through as a header.  Since OK is the default
4370
            // just do nothing.
4371
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4372
            // $this->outgoing_headers[] = "Status: 200 OK";
4373
        }
4374
        // add debug data if in debug mode
4375
        if (isset($this->debug_flag) && $this->debug_flag) {
4376
            $payload .= $this->getDebugAsXMLComment();
4377
        }
4378
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4379
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4380
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . $rev[1] . ')';
4381
        // Let the Web server decide about this
4382
        //$this->outgoing_headers[] = "Connection: Close\r\n";
4383
        $payload                  = $this->getHTTPBody($payload);
4384
        $type                     = $this->getHTTPContentType();
4385
        $charset                  = $this->getHTTPContentTypeCharset();
4386
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4387
        //begin code to compress payload - by John
4388
        // NOTE: there is no way to know whether the Web server will also compress
4389
        // this data.
4390
        if (mb_strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4391
            if (false !== mb_strpos($this->headers['accept-encoding'], 'gzip')) {
4392
                if (function_exists('gzencode')) {
4393
                    if (isset($this->debug_flag) && $this->debug_flag) {
4394
                        $payload .= '<!-- Content being gzipped -->';
4395
                    }
4396
                    $this->outgoing_headers[] = 'Content-Encoding: gzip';
4397
                    $payload                  = gzencode($payload);
4398
                } else {
4399
                    if (isset($this->debug_flag) && $this->debug_flag) {
4400
                        $payload .= '<!-- Content will not be gzipped: no gzencode -->';
4401
                    }
4402
                }
4403
            } elseif (false !== mb_strpos($this->headers['accept-encoding'], 'deflate')) {
4404
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4405
                // instead of gzcompress output,
4406
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4407
                if (function_exists('gzdeflate')) {
4408
                    if (isset($this->debug_flag) && $this->debug_flag) {
4409
                        $payload .= '<!-- Content being deflated -->';
4410
                    }
4411
                    $this->outgoing_headers[] = 'Content-Encoding: deflate';
4412
                    $payload                  = gzdeflate($payload);
4413
                } else {
4414
                    if (isset($this->debug_flag) && $this->debug_flag) {
4415
                        $payload .= '<!-- Content will not be deflated: no gzcompress -->';
4416
                    }
4417
                }
4418
            }
4419
        }
4420
        //end code
4421
        $this->outgoing_headers[] = 'Content-Length: ' . mb_strlen($payload);
4422
        reset($this->outgoing_headers);
4423
        foreach ($this->outgoing_headers as $hdr) {
4424
            header($hdr, false);
4425
        }
4426
        print $payload;
4427
        $this->response = implode("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
4428
    }
4429
4430
    /**
4431
     * takes the value that was created by parsing the request
4432
     * and compares to the method's signature, if available.
4433
     *
4434
     * @param  string $operation The operation to be invoked
4435
     * @param  array  $request   The array of parameter values
4436
     * @return bool Whether the operation was found
4437
     */
4438
    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

4438
    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...
4439
    {
4440
        if (isset($this->wsdl) && is_object($this->wsdl)) {
4441
            if ($this->wsdl->getOperationData($operation)) {
4442
                return true;
4443
            }
4444
        } elseif (isset($this->operations[$operation])) {
4445
            return true;
4446
        }
4447
4448
        return false;
4449
    }
4450
4451
    /**
4452
     * processes SOAP message received from client
4453
     *
4454
     * @param  array  $headers The HTTP headers
4455
     * @param  string $data    unprocessed request data from client
4456
     * @return mixed  value of the message, decoded into a PHP type
4457
     */
4458
    private function parseRequest($headers, $data)
4459
    {
4460
        $this->debug('Entering parseRequest() for data of length ' . mb_strlen($data) . ' headers:');
4461
        $this->appendDebug($this->varDump($headers));
4462
        if (!isset($headers['content-type'])) {
4463
            $this->setError('Request not of type text/xml (no content-type header)');
4464
4465
            return false;
4466
        }
4467
        if (false === mb_strpos($headers['content-type'], 'text/xml')) {
4468
            $this->setError('Request not of type text/xml');
4469
4470
            return false;
4471
        }
4472
        if (mb_strpos($headers['content-type'], '=')) {
4473
            $enc = str_replace('"', '', mb_substr(mb_strstr($headers['content-type'], '='), 1));
4474
            $this->debug('Got response encoding: ' . $enc);
4475
            $this->xml_encoding = 'US-ASCII';
4476
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
4477
                $this->xml_encoding = mb_strtoupper($enc);
4478
            }
4479
        } else {
4480
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4481
            $this->xml_encoding = 'ISO-8859-1';
4482
        }
4483
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4484
        // parse response, get soap parser obj
4485
        $parser = new Nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
4486
        // parser debug
4487
        $this->debug("parser debug: \n" . $parser->getDebug());
4488
        // if fault occurred during message parsing
4489
        if (false !== ($err = $parser->getError())) {
4490
            $this->result = 'fault: error in msg parsing: ' . $err;
4491
            $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
4492
            // else successfully parsed request into soapval object
4493
        } else {
4494
            // get/set methodname
4495
            $this->methodURI  = $parser->root_struct_namespace;
4496
            $this->methodname = $parser->root_struct_name;
4497
            $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4498
            $this->debug('calling parser->get_soapbody()');
4499
            $this->methodparams = $parser->get_soapbody();
4500
            // get SOAP headers
4501
            $this->requestHeaders = $parser->getHeaders();
4502
            // get SOAP Header
4503
            $this->requestHeader = $parser->get_soapheader();
4504
            // add document for doclit support
4505
            $this->document = $parser->document;
4506
        }
4507
    }
4508
4509
    /**
4510
     * gets the HTTP body for the current response.
4511
     *
4512
     * @param  string $soapmsg The SOAP payload
4513
     * @return string The HTTP body, which includes the SOAP payload
4514
     */
4515
    private function getHTTPBody($soapmsg)
4516
    {
4517
        return $soapmsg;
4518
    }
4519
4520
    /**
4521
     * gets the HTTP content type for the current response.
4522
     *
4523
     * Note: getHTTPBody must be called before this.
4524
     *
4525
     * @return string the HTTP content type for the current response.
4526
     */
4527
    private function getHTTPContentType()
4528
    {
4529
        return 'text/xml';
4530
    }
4531
4532
    /**
4533
     * gets the HTTP content type charset for the current response.
4534
     * returns false for non-text content types.
4535
     *
4536
     * Note: getHTTPBody must be called before this.
4537
     *
4538
     * @return string the HTTP content type charset for the current response.
4539
     */
4540
    private function getHTTPContentTypeCharset()
4541
    {
4542
        return $this->soap_defencoding;
4543
    }
4544
4545
    /**
4546
     * add a method to the dispatch map (this has been replaced by the register method)
4547
     *
4548
     * @param string $methodname
4549
     * @param string $in  array of input values
4550
     * @param string $out array of output values
4551
     * @deprecated
4552
     */
4553
    public function add_to_map($methodname, $in, $out)
4554
    {
4555
        $this->operations[$methodname] = ['name' => $methodname, 'in' => $in, 'out' => $out];
4556
    }
4557
4558
    /**
4559
     * register a service function with the server
4560
     *
4561
     * @param string $name          the name of the PHP function, class.method or class..method
4562
     * @param array  $in            assoc array of input values: key = param name, value = param type
4563
     * @param array  $out           assoc array of output values: key = param name, value = param type
4564
     * @param mixed  $namespace     the element namespace for the method or false
4565
     * @param mixed  $soapaction    the soapaction for the method or false
4566
     * @param mixed  $style         optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4567
     * @param mixed  $use           optional (encoded|literal) or false
4568
     * @param string $documentation optional Description to include in WSDL
4569
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4570
     * @return bool
4571
     */
4572
    public function register(
4573
        $name,
4574
        $in = [],
4575
        $out = [],
4576
        $namespace = false,
4577
        $soapaction = false,
4578
        $style = false,
4579
        $use = false,
4580
        $documentation = '',
4581
        $encodingStyle = '')
4582
    {
4583
        global $HTTP_SERVER_VARS;
4584
4585
        if ($this->externalWSDLURL) {
4586
            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...
4587
        }
4588
        if (!$name) {
4589
            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...
4590
        }
4591
        if (!is_array($in)) {
0 ignored issues
show
introduced by
The condition is_array($in) is always true.
Loading history...
4592
            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...
4593
        }
4594
        if (!is_array($out)) {
0 ignored issues
show
introduced by
The condition is_array($out) is always true.
Loading history...
4595
            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...
4596
        }
4597
        if (false === $namespace) {
4598
        }
4599
        if (false === $soapaction) {
4600
            if (isset($_SERVER)) {
4601
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
4602
                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4603
                $HTTPS       = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4604
            } elseif (isset($HTTP_SERVER_VARS)) {
4605
                $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4606
                $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4607
                $HTTPS       = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4608
            } else {
4609
                $this->setError('Neither _SERVER nor HTTP_SERVER_VARS is available');
4610
            }
4611
            $SCHEME = 'http';
4612
            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...
4613
                $SCHEME = 'https';
4614
            }
4615
            $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$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...
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...
4616
        }
4617
        if (false === $style) {
4618
            $style = 'rpc';
4619
        }
4620
        if (false === $use) {
4621
            $use = 'encoded';
4622
        }
4623
        if ('encoded' === $use && '' === $encodingStyle) {
4624
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4625
        }
4626
4627
        $this->operations[$name] = [
4628
            'name'       => $name,
4629
            'in'         => $in,
4630
            'out'        => $out,
4631
            'namespace'  => $namespace,
4632
            'soapaction' => $soapaction,
4633
            'style'      => $style,
4634
        ];
4635
        if ($this->wsdl) {
4636
            $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
4637
        }
4638
4639
        return true;
4640
    }
4641
4642
    /**
4643
     * Specify a fault to be returned to the client.
4644
     * This also acts as a flag to the server that a fault has occured.
4645
     *
4646
     * @param string $faultcode
4647
     * @param string $faultstring
4648
     * @param string $faultactor
4649
     * @param string $faultdetail
4650
     */
4651
    public function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
4652
    {
4653
        if ('' === $faultdetail && $this->debug_flag) {
4654
            $faultdetail = $this->getDebug();
4655
        }
4656
        $this->fault                   = new Nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
4657
        $this->fault->soap_defencoding = $this->soap_defencoding;
4658
    }
4659
4660
    /**
4661
     * Sets up wsdl object.
4662
     * Acts as a flag to enable internal WSDL generation
4663
     *
4664
     * @param string $serviceName           , name of the service
4665
     * @param mixed  $namespace             optional 'tns' service namespace or false
4666
     * @param mixed  $endpoint              optional URL of service endpoint or false
4667
     * @param string $style                 optional (rpc|document) WSDL style (also specified by operation)
4668
     * @param string $transport             optional SOAP transport
4669
     * @param mixed  $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4670
     */
4671
    public function configureWSDL(
4672
        $serviceName,
4673
        $namespace = false,
4674
        $endpoint = false,
4675
        $style = 'rpc',
4676
        $transport = 'http://schemas.xmlsoap.org/soap/http',
4677
        $schemaTargetNamespace = false)
4678
    {
4679
        global $HTTP_SERVER_VARS;
4680
4681
        if (isset($_SERVER)) {
4682
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
4683
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
4684
            $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4685
            $HTTPS       = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4686
        } elseif (isset($HTTP_SERVER_VARS)) {
4687
            $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4688
            $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4689
            $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4690
            $HTTPS       = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4691
        } else {
4692
            $this->setError('Neither _SERVER nor HTTP_SERVER_VARS is available');
4693
        }
4694
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4695
        $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...
4696
        if ($colon) {
4697
            $SERVER_NAME = mb_substr($SERVER_NAME, 0, $colon);
4698
        }
4699
        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...
4700
            $SERVER_PORT = '';
4701
        } else {
4702
            $SERVER_PORT = ':' . $SERVER_PORT;
4703
        }
4704
        if (false === $namespace) {
4705
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4706
        }
4707
4708
        if (false === $endpoint) {
4709
            $SCHEME = 'http';
4710
            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...
4711
                $SCHEME = 'https';
4712
            }
4713
            $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...
4714
        }
4715
4716
        if (false === $schemaTargetNamespace) {
4717
            $schemaTargetNamespace = $namespace;
4718
        }
4719
4720
        $this->wsdl                     = new Wsdl();
4721
        $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...
4722
        $this->wsdl->endpoint           = $endpoint;
4723
        $this->wsdl->namespaces['tns']  = $namespace;
4724
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4725
        $this->wsdl->namespaces['Wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4726
        if ($schemaTargetNamespace != $namespace) {
4727
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4728
        }
4729
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new Nusoap_xmlschema('', '', $this->wsdl->namespaces);
4730
        if ('document' === $style) {
4731
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4732
        }
4733
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace                                   = $schemaTargetNamespace;
4734
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = [
4735
            'location' => '',
4736
            'loaded'   => true,
4737
        ];
4738
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0]          = [
4739
            'location' => '',
4740
            'loaded'   => true,
4741
        ];
4742
        $this->wsdl->bindings[$serviceName . 'Binding']                                                          = [
4743
            'name'      => $serviceName . 'Binding',
4744
            'style'     => $style,
4745
            'transport' => $transport,
4746
            'portType'  => $serviceName . 'PortType',
4747
        ];
4748
        $this->wsdl->ports[$serviceName . 'Port']                                                                = [
4749
            'binding'     => $serviceName . 'Binding',
4750
            'location'    => $endpoint,
4751
            'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/',
4752
        ];
4753
    }
4754
}
4755
4756
/**
4757
 * Backward compatibility
4758
 */
4759
class Soap_server extends Nusoap_server
4760
{
4761
}
4762
4763
/**
4764
 * parses a WSDL file, allows access to it's data, other utility methods.
4765
 * also builds WSDL structures programmatically.
4766
 *
4767
 * @author   Dietrich Ayala <[email protected]>
4768
 * @author   Scott Nichol <[email protected]>
4769
 */
4770
class Wsdl extends Nusoap_base
4771
{
4772
    // URL or filename of the root of this WSDL
4773
    public $wsdl;
4774
    // define internal arrays of bindings, ports, operations, messages, etc.
4775
    public $schemas       = [];
4776
    public $currentSchema;
4777
    public $message       = [];
4778
    public $complexTypes  = [];
4779
    public $messages      = [];
4780
    public $currentMessage;
4781
    public $currentOperation;
4782
    public $portTypes     = [];
4783
    public $currentPortType;
4784
    public $bindings      = [];
4785
    public $currentBinding;
4786
    public $ports         = [];
4787
    public $currentPort;
4788
    public $opData        = [];
4789
    public $status        = '';
4790
    public $documentation = false;
4791
    public $endpoint      = '';
4792
    // array of wsdl docs to import
4793
    public $import = [];
4794
    // parser vars
4795
    public $parser;
4796
    public $position    = 0;
4797
    public $depth       = 0;
4798
    public $depth_array = [];
4799
    // for getting wsdl
4800
    public $proxyhost        = '';
4801
    public $proxyport        = '';
4802
    public $proxyusername    = '';
4803
    public $proxypassword    = '';
4804
    public $timeout          = 0;
4805
    public $response_timeout = 30;
4806
    public $curl_options     = [];    // User-specified cURL options
4807
    public $use_curl         = false;            // whether to always try to use cURL
4808
    // for HTTP authentication
4809
    public $username    = '';                // Username for HTTP authentication
4810
    public $password    = '';                // Password for HTTP authentication
4811
    public $authtype    = '';                // Type of HTTP authentication
4812
    public $certRequest = [];        // Certificate for HTTP SSL authentication
4813
4814
    /**
4815
     * constructor
4816
     *
4817
     * @param string      $wsdl             WSDL document URL
4818
     * @param bool|string $proxyhost
4819
     * @param bool|string $proxyport
4820
     * @param bool|string $proxyusername
4821
     * @param bool|string $proxypassword
4822
     * @param int         $timeout          set the connection timeout
4823
     * @param int         $response_timeout set the response timeout
4824
     * @param array       $curl_options     user-specified cURL options
4825
     * @param bool        $use_curl         try to use cURL
4826
     */
4827
    public function __construct(
4828
        $wsdl = '',
4829
        $proxyhost = false,
4830
        $proxyport = false,
4831
        $proxyusername = false,
4832
        $proxypassword = false,
4833
        $timeout = 0,
4834
        $response_timeout = 30,
4835
        $curl_options = null,
4836
        $use_curl = false)
4837
    {
4838
        parent::__construct();
4839
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4840
        $this->proxyhost        = $proxyhost;
4841
        $this->proxyport        = $proxyport;
4842
        $this->proxyusername    = $proxyusername;
4843
        $this->proxypassword    = $proxypassword;
4844
        $this->timeout          = $timeout;
4845
        $this->response_timeout = $response_timeout;
4846
        if (is_array($curl_options)) {
4847
            $this->curl_options = $curl_options;
4848
        }
4849
        $this->use_curl = $use_curl;
4850
        $this->fetchWSDL($wsdl);
4851
    }
4852
4853
    /**
4854
     * fetches the WSDL document and parses it
4855
     *
4856
     * @param $wsdl
4857
     */
4858
    public function fetchWSDL($wsdl)
4859
    {
4860
        $this->debug("parse and process WSDL path=$wsdl");
4861
        $this->wsdl = $wsdl;
4862
        // parse wsdl file
4863
        if ('' !== $this->wsdl) {
4864
            $this->parseWSDL($this->wsdl);
4865
        }
4866
        // imports
4867
        // TODO: handle imports more properly, grabbing them in-line and nesting them
4868
        $imported_urls = [];
4869
        $imported      = 1;
4870
        while ($imported > 0) {
4871
            $imported = 0;
4872
            // Schema imports
4873
            foreach ($this->schemas as $ns => $list) {
4874
                foreach ($list as $xs) {
4875
                    $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4876
                    foreach ($xs->imports as $ns2 => $list2) {
4877
                        for ($ii = 0, $iiMax = count($list2); $ii < $iiMax; ++$ii) {
4878
                            if (!$list2[$ii]['loaded']) {
4879
                                $this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;
4880
                                $url                                                    = $list2[$ii]['location'];
4881
                                if ('' !== $url) {
4882
                                    $urlparts = parse_url($url);
4883
                                    if (!isset($urlparts['host'])) {
4884
                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . mb_substr($wsdlparts['path'], 0, mb_strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4885
                                    }
4886
                                    if (!in_array($url, $imported_urls)) {
4887
                                        $this->parseWSDL($url);
4888
                                        ++$imported;
4889
                                        $imported_urls[] = $url;
4890
                                    }
4891
                                } else {
4892
                                    $this->debug('Unexpected scenario: empty URL for unloaded import');
4893
                                }
4894
                            }
4895
                        }
4896
                    }
4897
                }
4898
            }
4899
            // WSDL imports
4900
            $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4901
            foreach ($this->import as $ns => $list) {
4902
                for ($ii = 0, $iiMax = count($list); $ii < $iiMax; ++$ii) {
4903
                    if (!$list[$ii]['loaded']) {
4904
                        $this->import[$ns][$ii]['loaded'] = true;
4905
                        $url                              = $list[$ii]['location'];
4906
                        if ('' !== $url) {
4907
                            $urlparts = parse_url($url);
4908
                            if (!isset($urlparts['host'])) {
4909
                                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . mb_substr($wsdlparts['path'], 0, mb_strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4910
                            }
4911
                            if (!in_array($url, $imported_urls)) {
4912
                                $this->parseWSDL($url);
4913
                                ++$imported;
4914
                                $imported_urls[] = $url;
4915
                            }
4916
                        } else {
4917
                            $this->debug('Unexpected scenario: empty URL for unloaded import');
4918
                        }
4919
                    }
4920
                }
4921
            }
4922
        }
4923
        // add new data to operation data
4924
        foreach ($this->bindings as $binding => $bindingData) {
4925
            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4926
                foreach ($bindingData['operations'] as $operation => $data) {
4927
                    $this->debug('post-parse data gathering for ' . $operation);
4928
                    $this->bindings[$binding]['operations'][$operation]['input']  = isset($this->bindings[$binding]['operations'][$operation]['input']) ? array_merge($this->bindings[$binding]['operations'][$operation]['input'],
4929
                                                                                                                                                                      $this->portTypes[$bindingData['portType']][$operation]['input']) : $this->portTypes[$bindingData['portType']][$operation]['input'];
4930
                    $this->bindings[$binding]['operations'][$operation]['output'] = isset($this->bindings[$binding]['operations'][$operation]['output']) ? array_merge($this->bindings[$binding]['operations'][$operation]['output'],
4931
                                                                                                                                                                       $this->portTypes[$bindingData['portType']][$operation]['output']) : $this->portTypes[$bindingData['portType']][$operation]['output'];
4932
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
4933
                        $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
4934
                    }
4935
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
4936
                        $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
4937
                    }
4938
                    // Set operation style if necessary, but do not override one already provided
4939
                    if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4940
                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4941
                    }
4942
                    $this->bindings[$binding]['operations'][$operation]['transport']     = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4943
                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
4944
                    $this->bindings[$binding]['operations'][$operation]['endpoint']      = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4945
                }
4946
            }
4947
        }
4948
    }
4949
4950
    /**
4951
     * parses the wsdl document
4952
     *
4953
     * @param string $wsdl path or URL
4954
     * @return bool
4955
     */
4956
    private function parseWSDL($wsdl = '')
4957
    {
4958
        $this->debug("parse WSDL at path=$wsdl");
4959
4960
        if ('' === $wsdl) {
4961
            $this->debug('no wsdl passed to parseWSDL()!!');
4962
            $this->setError('no wsdl passed to parseWSDL()!!');
4963
4964
            return false;
4965
        }
4966
4967
        // parse $wsdl for url format
4968
        $wsdl_props = parse_url($wsdl);
4969
4970
        if (isset($wsdl_props['scheme']) && ('http' === $wsdl_props['scheme'] || 'https' === $wsdl_props['scheme'])) {
4971
            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4972
            // get wsdl
4973
            $tr                 = new Soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4974
            $tr->request_method = 'GET';
4975
            $tr->useSOAPAction  = false;
4976
            if ($this->proxyhost && $this->proxyport) {
4977
                $tr->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
4978
            }
4979
            if ('' !== $this->authtype) {
4980
                $tr->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
4981
            }
4982
            $tr->setEncoding('gzip, deflate');
4983
            $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4984
            //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4985
            //$this->debug("WSDL response\n" . $tr->incoming_payload);
4986
            $this->appendDebug($tr->getDebug());
4987
            // catch errors
4988
            if (false !== ($err = $tr->getError())) {
4989
                $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: ' . $err;
4990
                $this->debug($errstr);
4991
                $this->setError($errstr);
4992
                unset($tr);
4993
4994
                return false;
4995
            }
4996
            unset($tr);
4997
            $this->debug('got WSDL URL');
4998
        } else {
4999
            // $wsdl is not http(s), so treat it as a file URL or plain file path
5000
            $path = $wsdl;
5001
            if (isset($wsdl_props['scheme']) && ('file' === $wsdl_props['scheme']) && isset($wsdl_props['path'])) {
5002
                $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
5003
            }
5004
            $this->debug('getting WSDL file ' . $path);
5005
            if (false !== ($fp = @fopen($path, 'rb'))) {
5006
                $wsdl_string = '';
5007
                while (false !== ($data = fread($fp, 32768))) {
5008
                    $wsdl_string .= $data;
5009
                }
5010
                fclose($fp);
5011
            } else {
5012
                $errstr = "Bad path to WSDL file $path";
5013
                $this->debug($errstr);
5014
                $this->setError($errstr);
5015
5016
                return false;
5017
            }
5018
        }
5019
        $this->debug('Parse WSDL');
5020
        // end new code added
5021
        // Create an XML parser.
5022
        $this->parser = xml_parser_create();
5023
        // Set the options for parsing the XML data.
5024
        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
5025
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
5026
        // Set the object for the parser.
5027
        xml_set_object($this->parser, $this);
5028
        // Set the element handlers for the parser.
5029
        xml_set_element_handler($this->parser, 'start_element', 'end_element');
5030
        xml_set_character_data_handler($this->parser, 'character_data');
5031
        // Parse the XML file.
5032
        if (!xml_parse($this->parser, $wsdl_string, true)) {
5033
            // Display an error message.
5034
            $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)));
5035
            $this->debug($errstr);
5036
            $this->debug("XML payload:\n" . $wsdl_string);
5037
            $this->setError($errstr);
5038
            xml_parser_free($this->parser);
5039
            unset($this->parser);
5040
5041
            return false;
5042
        }
5043
        // free the parser
5044
        xml_parser_free($this->parser);
5045
        unset($this->parser);
5046
        $this->debug('Parsing WSDL done');
5047
        // catch wsdl parse errors
5048
        if ($this->getError()) {
5049
            return false;
5050
        }
5051
5052
        return true;
5053
    }
5054
5055
    /**
5056
     * start-element handler
5057
     *
5058
     * @param string       $parser XML parser object
5059
     * @param string       $name   element name
5060
     * @param string|array $attrs  associative array of attributes
5061
     */
5062
    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...
5063
    {
5064
        if ('schema' === $this->status) {
5065
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
5066
            $this->appendDebug($this->currentSchema->getDebug());
5067
            $this->currentSchema->clearDebug();
5068
        } elseif (preg_match('/schema$/', $name)) {
5069
            $this->debug('Parsing WSDL schema');
5070
            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
5071
            $this->status        = 'schema';
5072
            $this->currentSchema = new Nusoap_xmlschema('', '', $this->namespaces);
5073
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
5074
            $this->appendDebug($this->currentSchema->getDebug());
5075
            $this->currentSchema->clearDebug();
5076
        } else {
5077
            // position in the total number of elements, starting from 0
5078
            $pos   = $this->position++;
5079
            $depth = $this->depth++;
5080
            // set self as current value for this depth
5081
            $this->depth_array[$depth] = $pos;
5082
            $this->message[$pos]       = ['cdata' => ''];
5083
            // process attributes
5084
            if ($attrs && is_array($attrs)) {
5085
                // register namespace declarations
5086
                foreach ($attrs as $k => $v) {
5087
                    if (preg_match('/^xmlns/', $k)) {
5088
                        if (false !== ($ns_prefix = mb_substr(mb_strrchr($k, ':'), 1))) {
5089
                            $this->namespaces[$ns_prefix] = $v;
5090
                        } else {
5091
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
5092
                        }
5093
                        if ('http://www.w3.org/2001/XMLSchema' === $v || 'http://www.w3.org/1999/XMLSchema' === $v || 'http://www.w3.org/2000/10/XMLSchema' === $v) {
5094
                            $this->XMLSchemaVersion  = $v;
5095
                            $this->namespaces['xsi'] = $v . '-instance';
5096
                        }
5097
                    }
5098
                }
5099
                // expand each attribute prefix to its namespace
5100
                foreach ($attrs as $k => $v) {
5101
                    $k = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
5102
                    if ('location' !== $k && 'soapAction' !== $k && 'namespace' !== $k) {
5103
                        $v = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
5104
                    }
5105
                    $eAttrs[$k] = $v;
5106
                }
5107
                $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 5100. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
5108
            } else {
5109
                $attrs = [];
5110
            }
5111
            // Set default prefix and namespace
5112
            // to prevent error Undefined variable $prefix and $namespace if (preg_match('/:/', $name)) return 0 or FALSE
5113
            $prefix    = '';
5114
            $namespace = '';
5115
            // get element prefix, namespace and name
5116
            if (preg_match('/:/', $name)) {
5117
                // get ns prefix
5118
                $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
5119
                // get ns
5120
                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
5121
                // get unqualified name
5122
                $name = mb_substr(mb_strstr($name, ':'), 1);
5123
            }
5124
            // process attributes, expanding any prefixes to namespaces
5125
            // find status, register data
5126
            switch ($this->status) {
5127
                case 'message':
5128
5129
                    if ('part' === $name) {
5130
                        if (isset($attrs['type'])) {
5131
                            $this->debug('msg ' . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
5132
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
5133
                        }
5134
                        if (isset($attrs['element'])) {
5135
                            $this->debug('msg ' . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
5136
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
5137
                        }
5138
                    }
5139
5140
                    break;
5141
                case 'portType':
5142
5143
                    switch ($name) {
5144
                        case 'operation':
5145
5146
                            $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...
5147
                            $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
5148
                            if (isset($attrs['parameterOrder'])) {
5149
                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
5150
                            }
5151
5152
                            break;
5153
                        case 'documentation':
5154
5155
                            $this->documentation = true;
5156
5157
                            break;
5158
                        // merge input/output data
5159
                        default:
5160
5161
                            $m                                                                                      = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
5162
                            $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
5163
5164
                            break;
5165
                    }
5166
5167
                    break;
5168
                case 'binding':
5169
5170
                    switch ($name) {
5171
                        case 'binding':
5172
                            // get ns prefix
5173
5174
                            if (isset($attrs['style'])) {
5175
                                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
5176
                            }
5177
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
5178
5179
                            break;
5180
                        case 'header':
5181
5182
                            $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
5183
5184
                            break;
5185
                        case 'operation':
5186
5187
                            if (isset($attrs['soapAction'])) {
5188
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
5189
                            }
5190
                            if (isset($attrs['style'])) {
5191
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
5192
                            }
5193
                            if (isset($attrs['name'])) {
5194
                                $this->currentOperation = $attrs['name'];
5195
                                $this->debug("current binding operation: $this->currentOperation");
5196
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name']     = $attrs['name'];
5197
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding']  = $this->currentBinding;
5198
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
5199
                            }
5200
5201
                            break;
5202
                        case 'input':
5203
5204
                            $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...
5205
5206
                            break;
5207
                        case 'output':
5208
5209
                            $this->opStatus = 'output';
5210
5211
                            break;
5212
                        case 'body':
5213
5214
                            if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5215
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5216
                            } else {
5217
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5218
                            }
5219
5220
                            break;
5221
                    }
5222
5223
                    break;
5224
                case 'service':
5225
5226
                    switch ($name) {
5227
                        case 'port':
5228
5229
                            $this->currentPort = $attrs['name'];
5230
                            $this->debug('current port: ' . $this->currentPort);
5231
                            $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5232
5233
                            break;
5234
                        case 'address':
5235
5236
                            $this->ports[$this->currentPort]['location']                                = $attrs['location'];
5237
                            $this->ports[$this->currentPort]['bindingType']                             = $namespace;
5238
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
5239
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint']    = $attrs['location'];
5240
5241
                            break;
5242
                    }
5243
5244
                    break;
5245
            }
5246
            // set status
5247
            switch ($name) {
5248
                case 'import':
5249
5250
                    if (isset($attrs['location'])) {
5251
                        $this->import[$attrs['namespace']][] = ['location' => $attrs['location'], 'loaded' => false];
5252
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
5253
                    } else {
5254
                        $this->import[$attrs['namespace']][] = ['location' => '', 'loaded' => true];
5255
                        if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
5256
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
5257
                        }
5258
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
5259
                    }
5260
5261
                    break;
5262
                //wait for schema
5263
                //case 'types':
5264
                //  $this->status = 'schema';
5265
                //  break;
5266
                case 'message':
5267
5268
                    $this->status                   = 'message';
5269
                    $this->messages[$attrs['name']] = [];
5270
                    $this->currentMessage           = $attrs['name'];
5271
5272
                    break;
5273
                case 'portType':
5274
5275
                    $this->status                    = 'portType';
5276
                    $this->portTypes[$attrs['name']] = [];
5277
                    $this->currentPortType           = $attrs['name'];
5278
5279
                    break;
5280
                case 'binding':
5281
5282
                    if (isset($attrs['name'])) {
5283
                        // get binding name
5284
                        if (mb_strpos($attrs['name'], ':')) {
5285
                            $this->currentBinding = $this->getLocalPart($attrs['name']);
5286
                        } else {
5287
                            $this->currentBinding = $attrs['name'];
5288
                        }
5289
                        $this->status                                      = 'binding';
5290
                        $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5291
                        $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5292
                    }
5293
5294
                    break;
5295
                case 'service':
5296
5297
                    $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...
5298
                    $this->status      = 'service';
5299
                    $this->debug('current service: ' . $this->serviceName);
5300
5301
                    break;
5302
                case 'definitions':
5303
5304
                    foreach ($attrs as $name => $value) {
0 ignored issues
show
introduced by
$name is overwriting one of the parameters of this function.
Loading history...
5305
                        $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...
5306
                    }
5307
5308
                    break;
5309
            }
5310
        }
5311
    }
5312
5313
    /**
5314
     * end-element handler
5315
     *
5316
     * @param string $parser XML parser object
5317
     * @param string $name   element name
5318
     */
5319
    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...
5320
    {
5321
        // unset schema status
5322
        if (/*preg_match('/types$/', $name) ||*/
5323
        preg_match('/schema$/', $name)) {
5324
            $this->status = '';
5325
            $this->appendDebug($this->currentSchema->getDebug());
5326
            $this->currentSchema->clearDebug();
5327
            $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5328
            $this->debug('Parsing WSDL schema done');
5329
        }
5330
        if ('schema' === $this->status) {
5331
            $this->currentSchema->schemaEndElement($parser, $name);
5332
        } else {
5333
            // bring depth down a notch
5334
            $this->depth--;
5335
        }
5336
        // end documentation
5337
        if ($this->documentation) {
5338
            //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5339
            //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5340
            $this->documentation = false;
5341
        }
5342
    }
5343
5344
    /**
5345
     * element content handler
5346
     *
5347
     * @param string $parser XML parser object
5348
     * @param string $data   element content
5349
     */
5350
    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

5350
    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...
5351
    {
5352
        $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5353
        if (isset($this->message[$pos]['cdata'])) {
5354
            $this->message[$pos]['cdata'] .= $data;
5355
        }
5356
        if ($this->documentation) {
5357
            $this->documentation .= $data;
5358
        }
5359
    }
5360
5361
    /**
5362
     * if authenticating, set user credentials here
5363
     *
5364
     * @param string $username
5365
     * @param string $password
5366
     * @param string $authtype    (basic|digest|certificate|ntlm)
5367
     * @param array  $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5368
     */
5369
    public function setCredentials($username, $password, $authtype = 'basic', $certRequest = [])
5370
    {
5371
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5372
        $this->appendDebug($this->varDump($certRequest));
5373
        $this->username    = $username;
5374
        $this->password    = $password;
5375
        $this->authtype    = $authtype;
5376
        $this->certRequest = $certRequest;
5377
    }
5378
5379
    /**
5380
     * @param $binding
5381
     * @return mixed
5382
     */
5383
    public function getBindingData($binding)
5384
    {
5385
        if (is_array($this->bindings[$binding])) {
5386
            return $this->bindings[$binding];
5387
        }
5388
    }
5389
5390
    /**
5391
     * returns an assoc array of operation names => operation data
5392
     *
5393
     * @param  string $portName    WSDL port name
5394
     * @param  string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5395
     * @return array
5396
     */
5397
    public function getOperations($portName = '', $bindingType = 'soap')
5398
    {
5399
        $ops = [];
5400
        if ('soap' === $bindingType) {
5401
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5402
        } elseif ('soap12' === $bindingType) {
5403
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5404
        } else {
5405
            $this->debug("getOperations bindingType $bindingType may not be supported");
5406
        }
5407
        $this->debug("getOperations for port '$portName' bindingType $bindingType");
5408
        // loop thru ports
5409
        foreach ($this->ports as $port => $portData) {
5410
            $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5411
            if ('' === $portName || $port == $portName) {
5412
                // binding type of port matches parameter
5413
                if ($portData['bindingType'] == $bindingType) {
5414
                    $this->debug("getOperations found port $port bindingType $bindingType");
5415
                    //$this->debug("port data: " . $this->varDump($portData));
5416
                    //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5417
                    // merge bindings
5418
                    if (isset($this->bindings[$portData['binding']]['operations'])) {
5419
                        $ops = array_merge($ops, $this->bindings[$portData['binding']]['operations']);
5420
                    }
5421
                }
5422
            }
5423
        }
5424
        if (0 === count($ops)) {
5425
            $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5426
        }
5427
5428
        return $ops;
5429
    }
5430
5431
    /**
5432
     * returns an associative array of data necessary for calling an operation
5433
     *
5434
     * @param  string $operation   name of operation
5435
     * @param  string $bindingType type of binding eg: soap, soap12
5436
     * @return array
5437
     */
5438
    public function getOperationData($operation, $bindingType = 'soap')
5439
    {
5440
        if ('soap' === $bindingType) {
5441
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5442
        } elseif ('soap12' === $bindingType) {
5443
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5444
        }
5445
        // loop thru ports
5446
        foreach ($this->ports as $port => $portData) {
5447
            // binding type of port matches parameter
5448
            if ($portData['bindingType'] == $bindingType) {
5449
                // get binding
5450
                //foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5451
                foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {
5452
                    // note that we could/should also check the namespace here
5453
                    if ($operation == $bOperation) {
5454
                        $opData = $this->bindings[$portData['binding']]['operations'][$operation];
5455
5456
                        return $opData;
5457
                    }
5458
                }
5459
            }
5460
        }
5461
    }
5462
5463
    /**
5464
     * returns an associative array of data necessary for calling an operation
5465
     *
5466
     * @param  string $soapAction  soapAction for operation
5467
     * @param  string $bindingType type of binding eg: soap, soap12
5468
     * @return array
5469
     */
5470
    public function getOperationDataForSoapAction($soapAction, $bindingType = 'soap')
5471
    {
5472
        if ('soap' === $bindingType) {
5473
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5474
        } elseif ('soap12' === $bindingType) {
5475
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5476
        }
5477
        // loop thru ports
5478
        foreach ($this->ports as $port => $portData) {
5479
            // binding type of port matches parameter
5480
            if ($portData['bindingType'] == $bindingType) {
5481
                // loop through operations for the binding
5482
                foreach ($this->bindings[$portData['binding']]['operations'] as $bOperation => $opData) {
5483
                    if ($opData['soapAction'] == $soapAction) {
5484
                        return $opData;
5485
                    }
5486
                }
5487
            }
5488
        }
5489
    }
5490
5491
    /**
5492
     * returns an array of information about a given type
5493
     * returns false if no type exists by the given name
5494
     *
5495
     *    typeDef = array(
5496
     *    'elements' => array(), // refs to elements array
5497
     *   'restrictionBase' => '',
5498
     *   'phpType' => '',
5499
     *   'order' => '(sequence|all)',
5500
     *   'attrs' => array() // refs to attributes array
5501
     *   )
5502
     *
5503
     * @param  string $type the type
5504
     * @param  string $ns   namespace (not prefix) of the type
5505
     * @return mixed
5506
     * @see    nusoap_xmlschema
5507
     */
5508
    public function getTypeDef($type, $ns)
5509
    {
5510
        $this->debug("in getTypeDef: type=$type, ns=$ns");
5511
        if ((!$ns) && isset($this->namespaces['tns'])) {
5512
            $ns = $this->namespaces['tns'];
5513
            $this->debug("in getTypeDef: type namespace forced to $ns");
5514
        }
5515
        if (!isset($this->schemas[$ns])) {
5516
            foreach ($this->schemas as $ns0 => $schema0) {
5517
                if (0 == strcasecmp($ns, $ns0)) {
5518
                    $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5519
                    $ns = $ns0;
5520
                    break;
5521
                }
5522
            }
5523
        }
5524
        if (isset($this->schemas[$ns])) {
5525
            $this->debug("in getTypeDef: have schema for namespace $ns");
5526
            for ($i = 0, $iMax = count($this->schemas[$ns]); $i < $iMax; ++$i) {
5527
                $xs = $this->schemas[$ns][$i];
5528
                $t  = $xs->getTypeDef($type);
5529
                $this->appendDebug($xs->getDebug());
5530
                $xs->clearDebug();
5531
                if ($t) {
5532
                    $this->debug("in getTypeDef: found type $type");
5533
                    if (!isset($t['phpType'])) {
5534
                        // get info for type to tack onto the element
5535
                        $uqType = mb_substr($t['type'], mb_strrpos($t['type'], ':') + 1);
5536
                        $ns     = mb_substr($t['type'], 0, mb_strrpos($t['type'], ':'));
5537
                        $etype  = $this->getTypeDef($uqType, $ns);
5538
                        if ($etype) {
5539
                            $this->debug("found type for [element] $type:");
5540
                            $this->debug($this->varDump($etype));
5541
                            if (isset($etype['phpType'])) {
5542
                                $t['phpType'] = $etype['phpType'];
5543
                            }
5544
                            if (isset($etype['elements'])) {
5545
                                $t['elements'] = $etype['elements'];
5546
                            }
5547
                            if (isset($etype['attrs'])) {
5548
                                $t['attrs'] = $etype['attrs'];
5549
                            }
5550
                        } else {
5551
                            $this->debug("did not find type for [element] $type");
5552
                        }
5553
                    }
5554
5555
                    return $t;
5556
                }
5557
            }
5558
            $this->debug("in getTypeDef: did not find type $type");
5559
        } else {
5560
            $this->debug("in getTypeDef: do not have schema for namespace $ns");
5561
        }
5562
5563
        return false;
5564
    }
5565
5566
    /**
5567
     * prints html description of services
5568
     */
5569
    public function webDescription()
5570
    {
5571
        global $HTTP_SERVER_VARS;
5572
5573
        if (isset($_SERVER)) {
5574
            $PHP_SELF = $_SERVER['PHP_SELF'];
5575
        } elseif (isset($HTTP_SERVER_VARS)) {
5576
            $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5577
        } else {
5578
            $this->setError('Neither _SERVER nor HTTP_SERVER_VARS is available');
5579
        }
5580
5581
        $b = '
5582
        <html><head><title>NuSOAP: ' . $this->serviceName . '</title>
5583
        <style type="text/css">
5584
            body    { font-family: arial sans-serif; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5585
            p       { font-family: arial sans-serif; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5586
            pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5587
            ul      { margin-top: 10px; margin-left: 20px; }
5588
            li      { list-style-type: none; margin-top: 10px; color: #000000; }
5589
            .content{
5590
            margin-left: 0px; padding-bottom: 2em; }
5591
            .nav {
5592
            padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5593
            margin-top: 10px; margin-left: 0px; color: #000000;
5594
            background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5595
            .title {
5596
            font-family: arial sans-serif; font-size: 26px; color: #ffffff;
5597
            background-color: #999999; width: 100%;
5598
            margin-left: 0px; margin-right: 0px;
5599
            padding-top: 10px; padding-bottom: 10px;}
5600
            .hidden {
5601
            position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5602
            font-family: arial sans-serif; overflow: hidden; width: 600;
5603
            padding: 20px; font-size: 10px; background-color: #999999;
5604
            layer-background-color:#FFFFFF; }
5605
            a,a:active  { color: charcoal; font-weight: bold; }
5606
            a:visited   { color: #666666; font-weight: bold; }
5607
            a:hover     { color: cc3300; font-weight: bold; }
5608
        </style>
5609
        <script language="JavaScript" type="text/javascript">
5610
        <!--
5611
        // POP-UP CAPTIONS...
5612
        function lib_bwcheck(){ //Browsercheck (needed)
5613
            this.ver=navigator.appVersion
5614
            this.agent=navigator.userAgent
5615
            this.dom=document.getElementById?1:0
5616
            this.opera5=this.agent.indexOf("Opera 5")>-1
5617
            this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5618
            this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5619
            this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5620
            this.ie=this.ie4||this.ie5||this.ie6
5621
            this.mac=this.agent.indexOf("Mac")>-1
5622
            this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5623
            this.ns4=(document.layers && !this.dom)?1:0;
5624
            this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5625
5626
            return this
5627
        }
5628
        var bw = new Lib_bwcheck()
5629
        //Makes crossbrowser object.
5630
        function makeObj(obj){
5631
            this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5632
            if(!this.evnt) return false
5633
            this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5634
            this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5635
            this.writeIt=b_writeIt;
5636
5637
            return this
5638
        }
5639
        // A unit of measure that will be added when setting the position of a layer.
5640
        //var px = bw.ns4||window.opera?"":"px";
5641
        function b_writeIt(text){
5642
            if (bw.ns4) {this.wref.write(text);this.wref.close()}
5643
            else this.wref.innerHTML = text
5644
        }
5645
        //Shows the messages
5646
        var oDesc;
5647
        function popup(divid){
5648
            if (oDesc = new MakeObj(divid)) {
5649
            oDesc.css.visibility = "visible"
5650
            }
5651
        }
5652
        function popout(){ // Hides message
5653
            if(oDesc) oDesc.css.visibility = "hidden"
5654
        }
5655
        //-->
5656
        </script>
5657
        </head>
5658
        <body>
5659
        <div class=content>
5660
            <br><br>
5661
            <div class=title>' . $this->serviceName . '</div>
5662
            <div class=nav>
5663
                <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...
5664
                Click on an operation name to view it&apos;s details.</p>
5665
                <ul>';
5666
        foreach ($this->getOperations() as $op => $data) {
5667
            $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5668
            // create hidden div
5669
            $b .= "<div id='$op' class='hidden'>
5670
                    <a href='#' onclick='popout()'><span style='color: #ffffff; '>Close</span></a><br><br>";
5671
            foreach ($data as $donnie => $marie) {
5672
                // loop through opdata
5673
                if ('input' === $donnie || 'output' === $donnie) {
5674
                    // show input/output data
5675
                    $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
5676
                    foreach ($marie as $captain => $tenille) {
5677
                        // loop through data
5678
                        if ('parts' === $captain) {
5679
                            // loop thru parts
5680
                            $b .= "&nbsp;&nbsp;$captain:<br>";
5681
                            //if (is_array($tenille)) {
5682
                            foreach ($tenille as $joanie => $chachi) {
5683
                                $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
5684
                            }
5685
                            //}
5686
                        } else {
5687
                            $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
5688
                        }
5689
                    }
5690
                } else {
5691
                    $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
5692
                }
5693
            }
5694
            $b .= '</div>';
5695
        }
5696
        $b .= '
5697
                <ul>
5698
            </div>
5699
        </div></body></html>';
5700
5701
        return $b;
5702
    }
5703
5704
    /**
5705
     * serialize the parsed wsdl
5706
     *
5707
     * @param  mixed $debug whether to put debug=1 in endpoint URL
5708
     * @return string serialization of WSDL
5709
     */
5710
    public function serialize($debug = 0)
5711
    {
5712
        $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5713
        $xml .= "\n<definitions";
5714
        foreach ($this->namespaces as $k => $v) {
5715
            $xml .= " xmlns:$k=\"$v\"";
5716
        }
5717
        // 10.9.02 - add poulter fix for wsdl and tns declarations
5718
        if (isset($this->namespaces['wsdl'])) {
5719
            $xml .= ' xmlns="' . $this->namespaces['wsdl'] . '"';
5720
        }
5721
        if (isset($this->namespaces['tns'])) {
5722
            $xml .= ' targetNamespace="' . $this->namespaces['tns'] . '"';
5723
        }
5724
        $xml .= '>';
5725
        // imports
5726
        if ($this->import && is_array($this->import)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->import 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...
5727
            foreach ($this->import as $ns => $list) {
5728
                foreach ($list as $ii) {
5729
                    if ('' !== $ii['location']) {
5730
                        $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '">';
5731
                    } else {
5732
                        $xml .= '<import namespace="' . $ns . '">';
5733
                    }
5734
                }
5735
            }
5736
        }
5737
        // types
5738
        if (is_array($this->schemas) && count($this->schemas) >= 1) {
5739
            $xml .= "\n<types>\n";
5740
            foreach ($this->schemas as $ns => $list) {
5741
                foreach ($list as $xs) {
5742
                    $xml .= $xs->serializeSchema();
5743
                }
5744
            }
5745
            $xml .= '</types>';
5746
        }
5747
        // messages
5748
        if (is_array($this->messages) && count($this->messages) >= 1) {
5749
            foreach ($this->messages as $msgName => $msgParts) {
5750
                $xml .= "\n<message name=\"" . $msgName . '">';
5751
                if (is_array($msgParts)) {
5752
                    foreach ($msgParts as $partName => $partType) {
5753
                        // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5754
                        if (mb_strpos($partType, ':')) {
5755
                            $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

5755
                            $typePrefix = $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($partType));
Loading history...
5756
                        } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5757
                            // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5758
                            $typePrefix = 'xsd';
5759
                        } else {
5760
                            foreach ($this->typemap as $ns => $types) {
5761
                                if (isset($types[$partType])) {
5762
                                    $typePrefix = $this->getPrefixFromNamespace($ns);
5763
                                }
5764
                            }
5765
                            if (!isset($typePrefix)) {
5766
                                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...
5767
                            }
5768
                        }
5769
                        $ns        = $this->getNamespaceFromPrefix($typePrefix);
0 ignored issues
show
Bug introduced by
It seems like $typePrefix 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

5769
                        $ns        = $this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $typePrefix);
Loading history...
5770
                        $localPart = $this->getLocalPart($partType);
5771
                        $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

5771
                        $typeDef   = $this->getTypeDef($localPart, /** @scrutinizer ignore-type */ $ns);
Loading history...
5772
                        if ('element' === $typeDef['typeClass']) {
5773
                            $elementortype = 'element';
5774
                            if ('^' === mb_substr($localPart, -1)) {
5775
                                $localPart = mb_substr($localPart, 0, -1);
5776
                            }
5777
                        } else {
5778
                            $elementortype = 'type';
5779
                        }
5780
                        $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '">';
0 ignored issues
show
Bug introduced by
Are you sure $typePrefix of type false|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

5780
                        $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . /** @scrutinizer ignore-type */ $typePrefix . ':' . $localPart . '">';
Loading history...
5781
                    }
5782
                }
5783
                $xml .= '</message>';
5784
            }
5785
        }
5786
        // bindings & porttypes
5787
        if (is_array($this->bindings) && count($this->bindings) >= 1) {
5788
            $binding_xml  = '';
5789
            $portType_xml = '';
5790
            foreach ($this->bindings as $bindingName => $attrs) {
5791
                $binding_xml  .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5792
                $binding_xml  .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '">';
5793
                $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5794
                foreach ($attrs['operations'] as $opName => $opParts) {
5795
                    $binding_xml .= "\n" . '  <operation name="' . $opName . '">';
5796
                    $binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $opParts['style'] . '">';
5797
                    $enc_style   = '';
5798
                    if (isset($opParts['input']['encodingStyle']) && '' !== $opParts['input']['encodingStyle']) {
5799
                        $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5800
                    }
5801
                    $binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '></input>';
5802
                    $enc_style   = '';
5803
                    if (isset($opParts['output']['encodingStyle']) && '' !== $opParts['output']['encodingStyle']) {
5804
                        $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5805
                    }
5806
                    $binding_xml  .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '></output>';
5807
                    $binding_xml  .= "\n" . '  </operation>';
5808
                    $portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
5809
                    if (isset($opParts['parameterOrder'])) {
5810
                        $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5811
                    }
5812
                    $portType_xml .= '>';
5813
                    if (isset($opParts['documentation']) && '' !== $opParts['documentation']) {
5814
                        $portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation'], ENT_QUOTES | ENT_HTML5) . '</documentation>';
5815
                    }
5816
                    $portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '">';
5817
                    $portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '">';
5818
                    $portType_xml .= "\n" . '  </operation>';
5819
                }
5820
                $portType_xml .= "\n" . '</portType>';
5821
                $binding_xml  .= "\n" . '</binding>';
5822
            }
5823
            $xml .= $portType_xml . $binding_xml;
5824
        }
5825
        // services
5826
        $xml .= "\n<service name=\"" . $this->serviceName . '">';
5827
        if (is_array($this->ports) && count($this->ports) >= 1) {
5828
            foreach ($this->ports as $pName => $attrs) {
5829
                $xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5830
                $xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '">';
5831
                $xml .= "\n" . '  </port>';
5832
            }
5833
        }
5834
        $xml .= "\n" . '</service>';
5835
5836
        return $xml . "\n</definitions>";
5837
    }
5838
5839
    /**
5840
     * determine whether a set of parameters are unwrapped
5841
     * when they are expect to be wrapped, Microsoft-style.
5842
     *
5843
     * @param  string $type       the type (element name) of the wrapper
5844
     * @param  array  $parameters the parameter values for the SOAP call
5845
     * @return bool whether they parameters are unwrapped (and should be wrapped)
5846
     */
5847
    private function parametersMatchWrapped($type, &$parameters)
5848
    {
5849
        $this->debug("in parametersMatchWrapped type=$type, parameters=");
5850
        $this->appendDebug($this->varDump($parameters));
5851
5852
        // split type into namespace:unqualified-type
5853
        if (mb_strpos($type, ':')) {
5854
            $uqType = mb_substr($type, mb_strrpos($type, ':') + 1);
5855
            $ns     = mb_substr($type, 0, mb_strrpos($type, ':'));
5856
            $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5857
            if ($this->getNamespaceFromPrefix($ns)) {
5858
                $ns = $this->getNamespaceFromPrefix($ns);
5859
                $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5860
            }
5861
        } else {
5862
            // TODO: should the type be compared to types in XSD, and the namespace
5863
            // set to XSD if the type matches?
5864
            $this->debug("in parametersMatchWrapped: No namespace for type $type");
5865
            $ns     = '';
5866
            $uqType = $type;
5867
        }
5868
5869
        // get the type information
5870
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5871
            $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5872
5873
            return false;
5874
        }
5875
        $this->debug('in parametersMatchWrapped: found typeDef=');
5876
        $this->appendDebug($this->varDump($typeDef));
5877
        if ('^' === mb_substr($uqType, -1)) {
5878
            $uqType = mb_substr($uqType, 0, -1);
5879
        }
5880
        $phpType   = $typeDef['phpType'];
5881
        $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5882
        $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5883
5884
        // we expect a complexType or element of complexType
5885
        if ('struct' !== $phpType) {
5886
            $this->debug('in parametersMatchWrapped: not a struct');
5887
5888
            return false;
5889
        }
5890
5891
        // see whether the parameter names match the elements
5892
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5893
            $elements = 0;
5894
            $matches  = 0;
5895
            foreach ($typeDef['elements'] as $name => $attrs) {
5896
                if (isset($parameters[$name])) {
5897
                    $this->debug("in parametersMatchWrapped: have parameter named $name");
5898
                    ++$matches;
5899
                } else {
5900
                    $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5901
                }
5902
                ++$elements;
5903
            }
5904
5905
            $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5906
5907
            return !(0 == $matches);
5908
        }
5909
5910
        // since there are no elements for the type, if the user passed no
5911
        // parameters, the parameters match wrapped.
5912
        $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5913
5914
        return 0 === count($parameters);
5915
    }
5916
5917
    /**
5918
     * serialize PHP values according to a WSDL message definition
5919
     * contrary to the method name, this is not limited to RPC
5920
     *
5921
     * TODO
5922
     * - multi-ref serialization
5923
     * - validate PHP values against type definitions, return errors if invalid
5924
     *
5925
     * @param  string $operation   operation name
5926
     * @param  string $direction   (input|output)
5927
     * @param  mixed  $parameters  parameter value(s)
5928
     * @param  string $bindingType (soap|soap12)
5929
     * @return mixed  parameters serialized as XML or false on error (e.g. operation not found)
5930
     */
5931
    public function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap')
5932
    {
5933
        $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5934
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5935
5936
        if ('input' !== $direction && 'output' !== $direction) {
5937
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5938
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5939
5940
            return false;
5941
        }
5942
        if (!$opData = $this->getOperationData($operation, $bindingType)) {
5943
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5944
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5945
5946
            return false;
5947
        }
5948
        $this->debug('in serializeRPCParameters: opData:');
5949
        $this->appendDebug($this->varDump($opData));
5950
5951
        // Get encoding style for output and set to current
5952
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5953
        if (('input' === $direction) && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5954
            $encodingStyle = $opData['output']['encodingStyle'];
5955
            $enc_style     = $encodingStyle;
0 ignored issues
show
Unused Code introduced by
The assignment to $enc_style is dead and can be removed.
Loading history...
5956
        }
5957
5958
        // set input params
5959
        $xml = '';
5960
        if (isset($opData[$direction]['parts']) && count($opData[$direction]['parts']) > 0) {
5961
            $parts      = &$opData[$direction]['parts'];
5962
            $part_count = count($parts);
5963
            $style      = $opData['style'];
5964
            $use        = $opData[$direction]['use'];
5965
            $this->debug("have $part_count part(s) to serialize using $style/$use");
5966
            if (is_array($parameters)) {
5967
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5968
                $parameter_count     = count($parameters);
5969
                $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5970
                // check for Microsoft-style wrapped parameters
5971
                if ('document' === $style && 'literal' === $use && 1 == $part_count && isset($parts['parameters'])) {
5972
                    $this->debug('check whether the caller has wrapped the parameters');
5973
                    if ('output' === $direction && 'arraySimple' === $parametersArrayType && 1 == $parameter_count) {
5974
                        // TODO: consider checking here for double-wrapping, when
5975
                        // service function wraps, then NuSOAP wraps again
5976
                        $this->debug("change simple array to associative with 'parameters' element");
5977
                        $parameters['parameters'] = $parameters[0];
5978
                        unset($parameters[0]);
5979
                    }
5980
                    if (('arrayStruct' === $parametersArrayType || 0 == $parameter_count) && !isset($parameters['parameters'])) {
5981
                        $this->debug('check whether caller\'s parameters match the wrapped ones');
5982
                        if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5983
                            $this->debug('wrap the parameters for the caller');
5984
                            $parameters      = ['parameters' => $parameters];
5985
                            $parameter_count = 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $parameter_count is dead and can be removed.
Loading history...
5986
                        }
5987
                    }
5988
                }
5989
                foreach ($parts as $name => $type) {
5990
                    $this->debug("serializing part $name of type $type");
5991
                    // Track encoding style
5992
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5993
                        $encodingStyle = $opData[$direction]['encodingStyle'];
5994
                        $enc_style     = $encodingStyle;
5995
                    } else {
5996
                        $enc_style = false;
5997
                    }
5998
                    // NOTE: add error handling here
5999
                    // if serializeType returns false, then catch global error and fault
6000
                    if ('arraySimple' === $parametersArrayType) {
6001
                        $p = array_shift($parameters);
6002
                        $this->debug('calling serializeType w/indexed param');
6003
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
6004
                    } elseif (isset($parameters[$name])) {
6005
                        $this->debug('calling serializeType w/named param');
6006
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
6007
                    } else {
6008
                        // TODO: only send nillable
6009
                        $this->debug('calling serializeType w/null param');
6010
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
6011
                    }
6012
                }
6013
            } else {
6014
                $this->debug('no parameters passed.');
6015
            }
6016
        }
6017
        $this->debug("serializeRPCParameters returning: $xml");
6018
6019
        return $xml;
6020
    }
6021
6022
    /**
6023
     * serialize a PHP value according to a WSDL message definition
6024
     *
6025
     * TODO
6026
     * - multi-ref serialization
6027
     * - validate PHP values against type definitions, return errors if invalid
6028
     *
6029
     * @param  string $operation  operation name
6030
     * @param  string $direction  (input|output)
6031
     * @param  mixed  $parameters parameter value(s)
6032
     * @return mixed  parameters serialized as XML or false on error (e.g. operation not found)
6033
     * @deprecated
6034
     */
6035
    public function serializeParameters($operation, $direction, $parameters)
6036
    {
6037
        $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
6038
        $this->appendDebug('parameters=' . $this->varDump($parameters));
6039
6040
        if ('input' !== $direction && 'output' !== $direction) {
6041
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
6042
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
6043
6044
            return false;
6045
        }
6046
        if (!$opData = $this->getOperationData($operation)) {
6047
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
6048
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
6049
6050
            return false;
6051
        }
6052
        $this->debug('opData:');
6053
        $this->appendDebug($this->varDump($opData));
6054
6055
        // Get encoding style for output and set to current
6056
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6057
        if (('input' === $direction) && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
6058
            $encodingStyle = $opData['output']['encodingStyle'];
6059
            $enc_style     = $encodingStyle;
0 ignored issues
show
Unused Code introduced by
The assignment to $enc_style is dead and can be removed.
Loading history...
6060
        }
6061
6062
        // set input params
6063
        $xml = '';
6064
        if (isset($opData[$direction]['parts']) && count($opData[$direction]['parts']) > 0) {
6065
            $use = $opData[$direction]['use'];
6066
            $this->debug("use=$use");
6067
            $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
6068
            if (is_array($parameters)) {
6069
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
6070
                $this->debug('have ' . $parametersArrayType . ' parameters');
6071
                foreach ($opData[$direction]['parts'] as $name => $type) {
6072
                    $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
6073
                    // Track encoding style
6074
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
6075
                        $encodingStyle = $opData[$direction]['encodingStyle'];
6076
                        $enc_style     = $encodingStyle;
6077
                    } else {
6078
                        $enc_style = false;
6079
                    }
6080
                    // NOTE: add error handling here
6081
                    // if serializeType returns false, then catch global error and fault
6082
                    if ('arraySimple' === $parametersArrayType) {
6083
                        $p = array_shift($parameters);
6084
                        $this->debug('calling serializeType w/indexed param');
6085
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
6086
                    } elseif (isset($parameters[$name])) {
6087
                        $this->debug('calling serializeType w/named param');
6088
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
6089
                    } else {
6090
                        // TODO: only send nillable
6091
                        $this->debug('calling serializeType w/null param');
6092
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
6093
                    }
6094
                }
6095
            } else {
6096
                $this->debug('no parameters passed.');
6097
            }
6098
        }
6099
        $this->debug("serializeParameters returning: $xml");
6100
6101
        return $xml;
6102
    }
6103
6104
    /**
6105
     * serializes a PHP value according a given type definition
6106
     *
6107
     * @param  string      $name          name of value (part or element)
6108
     * @param  string      $type          XML schema type of value (type or element)
6109
     * @param  mixed       $value         a native PHP value (parameter value)
6110
     * @param  string      $use           use for part (encoded|literal)
6111
     * @param  bool|string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6112
     * @param bool         $unqualified   a kludge for what should be XML namespace form handling
6113
     * @return string      value serialized as an XML string
6114
     */
6115
    private function serializeType(
6116
        $name,
6117
        $type,
6118
        $value,
6119
        $use = 'encoded',
6120
        $encodingStyle = false,
6121
        $unqualified = false)
6122
    {
6123
        $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? 'unqualified' : 'qualified'));
6124
        $this->appendDebug('value=' . $this->varDump($value));
6125
        if ('encoded' === $use && $encodingStyle) {
6126
            $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

6126
            $encodingStyle = ' SOAP-ENV:encodingStyle="' . /** @scrutinizer ignore-type */ $encodingStyle . '"';
Loading history...
6127
        }
6128
6129
        // if a Soapval has been supplied, let its type override the WSDL
6130
        if (is_object($value) && 'Soapval' === get_class($value)) {
6131
            if ($value->type_ns) {
6132
                $type      = $value->type_ns . ':' . $value->type;
6133
                $forceType = true;
6134
                $this->debug("in serializeType: soapval overrides type to $type");
6135
            } elseif ($value->type) {
6136
                $type      = $value->type;
6137
                $forceType = true;
6138
                $this->debug("in serializeType: soapval overrides type to $type");
6139
            } else {
6140
                $forceType = false;
6141
                $this->debug('in serializeType: soapval does not override type');
6142
            }
6143
            $attrs = $value->attributes;
6144
            $value = $value->value;
6145
            $this->debug("in serializeType: soapval overrides value to $value");
6146
            if ($attrs) {
6147
                if (!is_array($value)) {
6148
                    $value['!'] = $value;
6149
                }
6150
                foreach ($attrs as $n => $v) {
6151
                    $value['!' . $n] = $v;
6152
                }
6153
                $this->debug('in serializeType: soapval provides attributes');
6154
            }
6155
        } else {
6156
            $forceType = false;
6157
        }
6158
6159
        $xml = '';
6160
        if (mb_strpos($type, ':')) {
6161
            $uqType = mb_substr($type, mb_strrpos($type, ':') + 1);
6162
            $ns     = mb_substr($type, 0, mb_strrpos($type, ':'));
6163
            $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
6164
            if ($this->getNamespaceFromPrefix($ns)) {
6165
                $ns = $this->getNamespaceFromPrefix($ns);
6166
                $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
6167
            }
6168
6169
            if ($ns == $this->XMLSchemaVersion || 'http://schemas.xmlsoap.org/soap/encoding/' === $ns) {
6170
                $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
6171
                $elementNS = '';
6172
                if ($unqualified && 'literal' === $use) {
6173
                    $elementNS = ' xmlns=""';
6174
                }
6175
                if (null === $value) {
6176
                    if ('literal' === $use) {
6177
                        // TODO: depends on minOccurs
6178
                        $xml = "<$name$elementNS>";
6179
                    } else {
6180
                        // TODO: depends on nillable, which should be checked before calling this method
6181
                        $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 false|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

6181
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace($ns) . ":$uqType\">";
Loading history...
6182
                    }
6183
                    $this->debug("in serializeType: returning: $xml");
6184
6185
                    return $xml;
6186
                }
6187
                if ('Array' === $uqType) {
6188
                    // JBoss/Axis does this sometimes
6189
                    return $this->serialize_val($value, $name, false, false, false, false, $use);
6190
                }
6191
                if ('boolean' === $uqType) {
6192
                    if ((is_string($value) && 'false' === $value) || (!$value)) {
6193
                        $value = 'false';
6194
                    } else {
6195
                        $value = 'true';
6196
                    }
6197
                }
6198
                if ('string' === $uqType && is_string($value)) {
6199
                    $value = $this->expandEntities($value);
6200
                }
6201
                if (('long' === $uqType || 'unsignedLong' === $uqType) && is_float($value)) {
6202
                    $value = sprintf('%.0lf', $value);
6203
                }
6204
                // it's a scalar
6205
                // TODO: what about null/nil values?
6206
                // check type isn't a custom type extending xmlschema namespace
6207
                if (!$this->getTypeDef($uqType, $ns)) {
6208
                    if ('literal' === $use) {
6209
                        $xml = "<$name$elementNS>$value</$name>";
6210
                        if ($forceType) {
6211
                            $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6212
                        }
6213
                    } else {
6214
                        $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6215
                    }
6216
                    $this->debug("in serializeType: returning: $xml");
6217
6218
                    return $xml;
6219
                }
6220
                $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
6221
            } elseif ('http://xml.apache.org/xml-soap' === $ns) {
6222
                $this->debug('in serializeType: appears to be Apache SOAP type');
6223
                if ('Map' === $uqType) {
6224
                    $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6225
                    if (!$tt_prefix) {
6226
                        $this->debug('in serializeType: Add namespace for Apache SOAP type');
6227
                        $tt_prefix                    = 'ns' . mt_rand(1000, 9999);
6228
                        $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
6229
                        // force this to be added to usedNamespaces
6230
                        $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6231
                    }
6232
                    $contents = '';
6233
                    foreach ($value as $k => $v) {
6234
                        $this->debug("serializing map element: key $k, value $v");
6235
                        $contents .= '<item>';
6236
                        $contents .= $this->serialize_val($k, 'key', false, false, false, false, $use);
6237
                        $contents .= $this->serialize_val($v, 'value', false, false, false, false, $use);
6238
                        $contents .= '</item>';
6239
                    }
6240
                    if ('literal' === $use) {
6241
                        $xml = "<$name>$contents</$name>";
6242
                        if ($forceType) {
6243
                            $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
6244
                        }
6245
                    } else {
6246
                        $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6247
                    }
6248
                    $this->debug("in serializeType: returning: $xml");
6249
6250
                    return $xml;
6251
                }
6252
                $this->debug('in serializeType: Apache SOAP type, but only support Map');
6253
            }
6254
        } else {
6255
            // TODO: should the type be compared to types in XSD, and the namespace
6256
            // set to XSD if the type matches?
6257
            $this->debug("in serializeType: No namespace for type $type");
6258
            $ns     = '';
6259
            $uqType = $type;
6260
        }
6261
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
6262
            $this->setError("$type ($uqType) is not a supported type.");
6263
            $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6264
6265
            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...
6266
        }
6267
6268
        $this->debug('in serializeType: found typeDef');
6269
        $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6270
        if ('^' === mb_substr($uqType, -1)) {
6271
            $uqType = mb_substr($uqType, 0, -1);
6272
        }
6273
        if (!isset($typeDef['phpType'])) {
6274
            $this->setError("$type ($uqType) has no phpType.");
6275
            $this->debug("in serializeType: $type ($uqType) has no phpType.");
6276
6277
            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...
6278
        }
6279
        $phpType = $typeDef['phpType'];
6280
        $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
6281
        // if php type == struct, map value to the <all> element names
6282
        if ('struct' === $phpType) {
6283
            if (isset($typeDef['typeClass']) && 'element' === $typeDef['typeClass']) {
6284
                $elementName = $uqType;
6285
                $elementNS   = ' xmlns=""';
6286
                if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6287
                    $elementNS = " xmlns=\"$ns\"";
6288
                }
6289
            } else {
6290
                $elementName = $name;
6291
                $elementNS   = '';
6292
                if ($unqualified) {
6293
                    $elementNS = ' xmlns=""';
6294
                }
6295
            }
6296
            if (null === $value) {
6297
                if ('literal' === $use) {
6298
                    // TODO: depends on minOccurs and nillable
6299
                    $xml = "<$elementName$elementNS>";
6300
                } else {
6301
                    $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6302
                }
6303
                $this->debug("in serializeType: returning: $xml");
6304
6305
                return $xml;
6306
            }
6307
            if (is_object($value)) {
6308
                $value = get_object_vars($value);
6309
            }
6310
            if (is_array($value)) {
6311
                $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6312
                if ('literal' === $use) {
6313
                    $xml = "<$elementName$elementNS$elementAttrs>";
6314
                    if ($forceType) {
6315
                        $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6316
                    }
6317
                } else {
6318
                    $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6319
                }
6320
6321
                if (isset($typeDef['simpleContent']) && 'true' === $typeDef['simpleContent']) {
6322
                    if (isset($value['!'])) {
6323
                        $xml .= $value['!'];
6324
                        $this->debug("in serializeType: serialized simpleContent for type $type");
6325
                    } else {
6326
                        $this->debug("in serializeType: no simpleContent to serialize for type $type");
6327
                    }
6328
                } else {
6329
                    // complexContent
6330
                    $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6331
                }
6332
                $xml .= "</$elementName>";
6333
            } else {
6334
                $this->debug('in serializeType: phpType is struct, but value is not an array');
6335
                $this->setError('phpType is struct, but value is not an array: see debug output for details');
6336
                $xml = '';
6337
            }
6338
        } elseif ('array' === $phpType) {
6339
            if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6340
                $elementNS = " xmlns=\"$ns\"";
6341
            } else {
6342
                $elementNS = '';
6343
                if ($unqualified) {
6344
                    $elementNS = ' xmlns=""';
6345
                }
6346
            }
6347
            if (null === $value) {
6348
                if ('literal' === $use) {
6349
                    // TODO: depends on minOccurs
6350
                    $xml = "<$name$elementNS>";
6351
                } else {
6352
                    $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\""
6353
                           . $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 false|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

6353
                           . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
Loading history...
6354
                           . ':Array" '
6355
                           . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6356
                           . ':arrayType="'
6357
                           . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
0 ignored issues
show
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

6357
                           . $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($typeDef['arrayType']))
Loading history...
Bug introduced by
Are you sure $this->getPrefixFromName...$typeDef['arrayType'])) of type false|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

6357
                           . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
Loading history...
6358
                           . ':'
6359
                           . $this->getLocalPart($typeDef['arrayType'])
6360
                           . '[0]"/>';
6361
                }
6362
                $this->debug("in serializeType: returning: $xml");
6363
6364
                return $xml;
6365
            }
6366
            if (isset($typeDef['multidimensional'])) {
6367
                $nv = [];
6368
                foreach ($value as $v) {
6369
                    $cols = ',' . count($v);
6370
                    $nv   = array_merge($nv, $v);
6371
                }
6372
                $value = $nv;
6373
            } else {
6374
                $cols = '';
6375
            }
6376
            if (is_array($value) && count($value) >= 1) {
6377
                $rows     = count($value);
6378
                $contents = '';
6379
                foreach ($value as $k => $v) {
6380
                    $this->debug("serializing array element: $k, " . (is_array($v) ? 'array' : $v) . " of type: $typeDef[arrayType]");
6381
                    //if (strpos($typeDef['arrayType'], ':') ) {
6382
                    if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6383
                        $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6384
                    } else {
6385
                        $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6386
                    }
6387
                }
6388
            } else {
6389
                $rows     = 0;
6390
                $contents = null;
6391
            }
6392
            // TODO: for now, an empty value will be serialized as a zero element
6393
            // array.  Revisit this when coding the handling of null/nil values.
6394
            if ('literal' === $use) {
6395
                $xml = "<$name$elementNS>" . $contents . "</$name>";
6396
            } else {
6397
                $xml = "<$name$elementNS xsi:type=\""
6398
                       . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6399
                       . ':Array" '
6400
                       . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6401
                       . ':arrayType="'
6402
                       . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6403
                       . ':'
6404
                       . $this->getLocalPart($typeDef['arrayType'])
6405
                       . "[$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...
6406
                       . $contents
6407
                       . "</$name>";
6408
            }
6409
        } elseif ('scalar' === $phpType) {
6410
            if (isset($typeDef['form']) && ('qualified' === $typeDef['form'])) {
6411
                $elementNS = " xmlns=\"$ns\"";
6412
            } else {
6413
                if ($unqualified) {
6414
                    $elementNS = ' xmlns=""';
6415
                } else {
6416
                    $elementNS = '';
6417
                }
6418
            }
6419
            if ('literal' === $use) {
6420
                if ($forceType) {
6421
                    $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6422
                } else {
6423
                    $xml = "<$name$elementNS>$value</$name>";
6424
                }
6425
            } else {
6426
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6427
            }
6428
        }
6429
        $this->debug("in serializeType: returning: $xml");
6430
6431
        return $xml;
6432
    }
6433
6434
    /**
6435
     * serializes the attributes for a complexType
6436
     *
6437
     * @param  array  $typeDef our internal representation of an XML schema type (or element)
6438
     * @param  mixed  $value   a native PHP value (parameter value)
6439
     * @param  string $ns      the namespace of the type
6440
     * @param  string $uqType  the local part of the type
6441
     * @return string value serialized as an XML string
6442
     */
6443
    private function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType)
6444
    {
6445
        $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6446
        $xml = '';
6447
        if (isset($typeDef['extensionBase'])) {
6448
            $nsx     = $this->getPrefix($typeDef['extensionBase']);
6449
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6450
            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

6450
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6451
                $nsx = $this->getNamespaceFromPrefix($nsx);
6452
            }
6453
            if (false !== ($typeDefx = $this->getTypeDef($uqTypex, $nsx))) {
6454
                $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6455
                $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6456
            } else {
6457
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6458
            }
6459
        }
6460
        if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6461
            $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6462
            if (is_array($value)) {
6463
                $xvalue = $value;
6464
            } elseif (is_object($value)) {
6465
                $xvalue = get_object_vars($value);
6466
            } else {
6467
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6468
                $xvalue = [];
6469
            }
6470
            foreach ($typeDef['attrs'] as $aName => $attrs) {
6471
                if (isset($xvalue['!' . $aName])) {
6472
                    $xname = '!' . $aName;
6473
                    $this->debug("value provided for attribute $aName with key $xname");
6474
                } elseif (isset($xvalue[$aName])) {
6475
                    $xname = $aName;
6476
                    $this->debug("value provided for attribute $aName with key $xname");
6477
                } elseif (isset($attrs['default'])) {
6478
                    $xname          = '!' . $aName;
6479
                    $xvalue[$xname] = $attrs['default'];
6480
                    $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6481
                } else {
6482
                    $xname = '';
6483
                    $this->debug("no value provided for attribute $aName");
6484
                }
6485
                if ($xname) {
6486
                    $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . '"';
6487
                }
6488
            }
6489
        } else {
6490
            $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6491
        }
6492
6493
        return $xml;
6494
    }
6495
6496
    /**
6497
     * serializes the elements for a complexType
6498
     *
6499
     * @param  array       $typeDef       our internal representation of an XML schema type (or element)
6500
     * @param  mixed       $value         a native PHP value (parameter value)
6501
     * @param  string      $ns            the namespace of the type
6502
     * @param  string      $uqType        the local part of the type
6503
     * @param  string      $use           use for part (encoded|literal)
6504
     * @param  bool|string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6505
     * @return string      value serialized as an XML string
6506
     */
6507
    private function serializeComplexTypeElements(
6508
        $typeDef,
6509
        $value,
6510
        $ns,
6511
        $uqType,
6512
        $use = 'encoded',
6513
        $encodingStyle = false)
6514
    {
6515
        $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6516
        $xml = '';
6517
        if (isset($typeDef['extensionBase'])) {
6518
            $nsx     = $this->getPrefix($typeDef['extensionBase']);
6519
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6520
            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

6520
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6521
                $nsx = $this->getNamespaceFromPrefix($nsx);
6522
            }
6523
            if (false !== ($typeDefx = $this->getTypeDef($uqTypex, $nsx))) {
6524
                $this->debug("serialize elements for extension base $nsx:$uqTypex");
6525
                $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
6526
            } else {
6527
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6528
            }
6529
        }
6530
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6531
            $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6532
            if (is_array($value)) {
6533
                $xvalue = $value;
6534
            } elseif (is_object($value)) {
6535
                $xvalue = get_object_vars($value);
6536
            } else {
6537
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6538
                $xvalue = [];
6539
            }
6540
            // toggle whether all elements are present - ideally should validate against schema
6541
            if (is_array($xvalue) && (count($typeDef['elements']) != count($xvalue))) {
6542
                $optionals = true;
6543
            }
6544
            foreach ($typeDef['elements'] as $eName => $attrs) {
6545
                if (!isset($xvalue[$eName])) {
6546
                    if (isset($attrs['default'])) {
6547
                        $xvalue[$eName] = $attrs['default'];
6548
                        $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6549
                    }
6550
                }
6551
                // if user took advantage of a minOccurs=0, then only serialize named parameters
6552
                if (isset($optionals) && !isset($xvalue[$eName]) && (!isset($attrs['nillable']) || 'true' !== $attrs['nillable'])) {
6553
                    if (isset($attrs['minOccurs']) && '0' != $attrs['minOccurs']) {
6554
                        $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6555
                    }
6556
                    // do nothing
6557
                    $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6558
                } else {
6559
                    // get value
6560
                    $v = null;
6561
                    if (isset($xvalue[$eName])) {
6562
                        $v = $xvalue[$eName];
6563
                    }
6564
                    $unqualified = false;
6565
                    if (isset($attrs['form'])) {
6566
                        $unqualified = ('unqualified' === $attrs['form']);
6567
                    }
6568
                    if (isset($attrs['maxOccurs']) && ('unbounded' === $attrs['maxOccurs'] || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && 'arraySimple' === $this->isArraySimpleOrStruct($v)) {
6569
                        $vv = $v;
6570
                        foreach ($vv as $k => $v) {
6571
                            if (isset($attrs['type']) || isset($attrs['ref'])) {
6572
                                // serialize schema-defined type
6573
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6574
                            } else {
6575
                                // serialize generic type (can this ever really happen?)
6576
                                $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6577
                                $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6578
                            }
6579
                        }
6580
                    } else {
6581
                        if (null === $v && isset($attrs['minOccurs']) && '0' == $attrs['minOccurs']) {
6582
                            // do nothing
6583
                        } elseif (null === $v && isset($attrs['nillable']) && 'true' === $attrs['nillable']) {
6584
                            // TODO: serialize a nil correctly, but for now serialize schema-defined type
6585
                            $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6586
                        } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
6587
                            // serialize schema-defined type
6588
                            $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6589
                        } else {
6590
                            // serialize generic type (can this ever really happen?)
6591
                            $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6592
                            $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6593
                        }
6594
                    }
6595
                }
6596
            }
6597
        } else {
6598
            $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6599
        }
6600
6601
        return $xml;
6602
    }
6603
6604
    /**
6605
     * adds an XML Schema complex type to the WSDL types
6606
     *
6607
     * @param string $name
6608
     * @param string $typeClass       (complexType|simpleType|attribute)
6609
     * @param string $phpType         currently supported are array and struct (php assoc array)
6610
     * @param string $compositor      (all|sequence|choice)
6611
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6612
     * @param array  $elements        e.g. array ( name => array(name=>'',type=>'') )
6613
     * @param array  $attrs           e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6614
     * @param string $arrayType       as namespace:name (xsd:string)
6615
     * @see    nusoap_xmlschema
6616
     */
6617
    public function addComplexType(
6618
        $name,
6619
        $typeClass = 'complexType',
6620
        $phpType = 'array',
6621
        $compositor = '',
6622
        $restrictionBase = '',
6623
        $elements = [],
6624
        $attrs = [],
6625
        $arrayType = '')
6626
    {
6627
        if ($elements && is_array($elements)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $elements 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...
6628
            $eElements = [];
6629
            foreach ($elements as $n => $e) {
6630
                // expand each element
6631
                $ee = [];
6632
                foreach ($e as $k => $v) {
6633
                    $k      = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
6634
                    $v      = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
6635
                    $ee[$k] = $v;
6636
                }
6637
                $eElements[$n] = $ee;
6638
            }
6639
            $elements = $eElements;
6640
        }
6641
6642
        if ($attrs && is_array($attrs)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attrs 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...
6643
            foreach ($attrs as $n => $a) {
6644
                // expand each attribute
6645
                foreach ($a as $k => $v) {
6646
                    $k      = mb_strpos($k, ':') ? $this->expandQname($k) : $k;
6647
                    $v      = mb_strpos($v, ':') ? $this->expandQname($v) : $v;
6648
                    $aa[$k] = $v;
6649
                }
6650
                $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...
6651
            }
6652
            $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 6643. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
6653
        }
6654
6655
        $restrictionBase = mb_strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6656
        $arrayType       = mb_strpos($arrayType, ':') ? $this->expandQname($arrayType) : $arrayType;
6657
6658
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6659
        $this->schemas[$typens][0]->addComplexType($name, $typeClass, $phpType, $compositor, $restrictionBase, $elements, $attrs, $arrayType);
6660
    }
6661
6662
    /**
6663
     * adds an XML Schema simple type to the WSDL types
6664
     *
6665
     * @param string $name
6666
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6667
     * @param string $typeClass       (should always be simpleType)
6668
     * @param string $phpType         (should always be scalar)
6669
     * @param array  $enumeration     array of values
6670
     * @see    nusoap_xmlschema
6671
     */
6672
    public function addSimpleType(
6673
        $name,
6674
        $restrictionBase = '',
6675
        $typeClass = 'simpleType',
6676
        $phpType = 'scalar',
6677
        $enumeration = [])
6678
    {
6679
        $restrictionBase = mb_strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6680
6681
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6682
        $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6683
    }
6684
6685
    /**
6686
     * adds an element to the WSDL types
6687
     *
6688
     * @param array $attrs attributes that must include name and type
6689
     * @see    nusoap_xmlschema
6690
     */
6691
    public function addElement($attrs)
6692
    {
6693
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6694
        $this->schemas[$typens][0]->addElement($attrs);
6695
    }
6696
6697
    /**
6698
     * register an operation with the server
6699
     *
6700
     * @param  string      $name          operation (method) name
6701
     * @param bool|array   $in            assoc array of input values: key = param name, value = param type
6702
     * @param bool|array   $out           assoc array of output values: key = param name, value = param type
6703
     * @param  bool|string $namespace     optional The namespace for the operation
6704
     * @param  bool|string $soapaction    optional The soapaction for the operation
6705
     * @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
6706
     * @param  string      $use           (encoded|literal) optional The use for the parameters (cannot mix right now)
6707
     * @param  string      $documentation optional The description to include in the WSDL
6708
     * @param  string      $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6709
     * @return bool
6710
     */
6711
    public function addOperation(
6712
        $name,
6713
        $in = false,
6714
        $out = false,
6715
        $namespace = false,
6716
        $soapaction = false,
6717
        $style = 'rpc',
6718
        $use = 'encoded',
6719
        $documentation = '',
6720
        $encodingStyle = '')
6721
    {
6722
        if ('encoded' === $use && '' === $encodingStyle) {
6723
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6724
        }
6725
6726
        if ('document' === $style) {
6727
            $elements = [];
6728
            foreach ($in as $n => $t) {
6729
                $elements[$n] = ['name' => $n, 'type' => $t, 'form' => 'unqualified'];
6730
            }
6731
            $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6732
            $this->addElement(['name' => $name, 'type' => $name . 'RequestType']);
6733
            $in = ['parameters' => 'tns:' . $name . '^'];
6734
6735
            $elements = [];
6736
            foreach ($out as $n => $t) {
6737
                $elements[$n] = ['name' => $n, 'type' => $t, 'form' => 'unqualified'];
6738
            }
6739
            $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6740
            $this->addElement(['name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified']);
6741
            $out = ['parameters' => 'tns:' . $name . 'Response' . '^'];
6742
        }
6743
6744
        // get binding
6745
        $this->bindings[$this->serviceName . 'Binding']['operations'][$name] = [
6746
            'name'          => $name,
6747
            'binding'       => $this->serviceName . 'Binding',
6748
            'endpoint'      => $this->endpoint,
6749
            'soapAction'    => $soapaction,
6750
            'style'         => $style,
6751
            'input'         => [
6752
                'use'           => $use,
6753
                'namespace'     => $namespace,
6754
                'encodingStyle' => $encodingStyle,
6755
                'message'       => $name . 'Request',
6756
                'parts'         => $in,
6757
            ],
6758
            'output'        => [
6759
                'use'           => $use,
6760
                'namespace'     => $namespace,
6761
                'encodingStyle' => $encodingStyle,
6762
                'message'       => $name . 'Response',
6763
                'parts'         => $out,
6764
            ],
6765
            'namespace'     => $namespace,
6766
            'transport'     => 'http://schemas.xmlsoap.org/soap/http',
6767
            'documentation' => $documentation,
6768
        ];
6769
        // add portTypes
6770
        // add messages
6771
        if ($in) {
6772
            foreach ($in as $pName => $pType) {
6773
                if (mb_strpos($pType, ':')) {
6774
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ':' . $this->getLocalPart($pType);
6775
                }
6776
                $this->messages[$name . 'Request'][$pName] = $pType;
6777
            }
6778
        } else {
6779
            $this->messages[$name . 'Request'] = '0';
6780
        }
6781
        if ($out) {
6782
            foreach ($out as $pName => $pType) {
6783
                if (mb_strpos($pType, ':')) {
6784
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ':' . $this->getLocalPart($pType);
6785
                }
6786
                $this->messages[$name . 'Response'][$pName] = $pType;
6787
            }
6788
        } else {
6789
            $this->messages[$name . 'Response'] = '0';
6790
        }
6791
6792
        return true;
6793
    }
6794
}
6795
6796
/**
6797
 * nusoap_parser class parses SOAP XML messages into native PHP values
6798
 *
6799
 * @author   Dietrich Ayala <[email protected]>
6800
 * @author   Scott Nichol <[email protected]>
6801
 */
6802
class Nusoap_parser extends Nusoap_base
6803
{
6804
    public $xml                   = '';
6805
    public $xml_encoding          = '';
6806
    public $method                = '';
6807
    public $root_struct           = '';
6808
    public $root_struct_name      = '';
6809
    public $root_struct_namespace = '';
6810
    public $root_header           = '';
6811
    public $document              = '';            // incoming SOAP body (text)
6812
    // determines where in the message we are (envelope,header,body,method)
6813
    public $status            = '';
6814
    public $position          = 0;
6815
    public $depth             = 0;
6816
    public $default_namespace = '';
6817
    //    public $namespaces = []; //Field 'namespaces' is already defined in \nusoap_base
6818
    public $message         = [];
6819
    public $parent          = '';
6820
    public $fault           = false;
6821
    public $fault_code      = '';
6822
    public $fault_str       = '';
6823
    public $fault_detail    = '';
6824
    public $depth_array     = [];
6825
    public $debug_flag      = true;
6826
    public $soapresponse;    // parsed SOAP Body
6827
    public $soapheader;        // parsed SOAP Header
6828
    public $responseHeaders = '';    // incoming SOAP headers (text)
6829
    public $body_position   = 0;
6830
    // for multiref parsing:
6831
    // array of id => pos
6832
    public $ids = [];
6833
    // array of id => hrefs => pos
6834
    public $multirefs = [];
6835
    // toggle for auto-decoding element content
6836
    public $decode_utf8 = true;
6837
6838
    /**
6839
     * constructor that actually does the parsing
6840
     *
6841
     * @param string          $xml         SOAP message
6842
     * @param string          $encoding    character encoding scheme of message
6843
     * @param    string|array $method      method for which XML is parsed (unused?)
6844
     * @param bool|string     $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6845
     */
6846
    public function __construct($xml, $encoding = 'UTF-8', $method = '', $decode_utf8 = true)
6847
    {
6848
        parent::__construct();
6849
        $this->xml          = $xml;
6850
        $this->xml_encoding = $encoding;
6851
        $this->method       = $method;
6852
        $this->decode_utf8  = $decode_utf8;
6853
6854
        // Check whether content has been read.
6855
        if (!empty($xml)) {
6856
            // Check XML encoding
6857
            $pos_xml = mb_strpos($xml, '<?xml');
6858
            if (false !== $pos_xml) {
6859
                $xml_decl = mb_substr($xml, $pos_xml, mb_strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6860
                if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6861
                    $xml_encoding = $res[1];
6862
                    if (mb_strtoupper($xml_encoding) != $encoding) {
6863
                        $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6864
                        $this->debug($err);
6865
                        if ('ISO-8859-1' !== $encoding || 'UTF-8' !== mb_strtoupper($xml_encoding)) {
6866
                            $this->setError($err);
6867
6868
                            return;
6869
                        }
6870
                        // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6871
                    } else {
6872
                        $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6873
                    }
6874
                } else {
6875
                    $this->debug('No encoding specified in XML declaration');
6876
                }
6877
            } else {
6878
                $this->debug('No XML declaration');
6879
            }
6880
            $this->debug('Entering nusoap_parser(), length=' . mb_strlen($xml) . ', encoding=' . $encoding);
6881
            // Create an XML parser - why not xml_parser_create_ns?
6882
            $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...
6883
            // Set the options for parsing the XML data.
6884
            //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6885
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6886
            xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6887
            // Set the object for the parser.
6888
            xml_set_object($this->parser, $this);
6889
            // Set the element handlers for the parser.
6890
            xml_set_element_handler($this->parser, 'start_element', 'end_element');
6891
            xml_set_character_data_handler($this->parser, 'character_data');
6892
6893
            // Parse the XML file.
6894
            if (!xml_parse($this->parser, $xml, true)) {
6895
                // Display an error message.
6896
                $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)));
6897
                $this->debug($err);
6898
                $this->debug("XML payload:\n" . $xml);
6899
                $this->setError($err);
6900
            } else {
6901
                $this->debug('in nusoap_parser ctor, message:');
6902
                $this->appendDebug($this->varDump($this->message));
6903
                $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
6904
                // get final value
6905
                $this->soapresponse = $this->message[$this->root_struct]['result'];
6906
                // get header value
6907
                if ('' !== $this->root_header && isset($this->message[$this->root_header]['result'])) {
6908
                    $this->soapheader = $this->message[$this->root_header]['result'];
6909
                }
6910
                // resolve hrefs/ids
6911
                if ($this->multirefs && is_array($this->multirefs)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->multirefs 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...
6912
                    foreach ($this->multirefs as $id => $hrefs) {
6913
                        $this->debug('resolving multirefs for id: ' . $id);
6914
                        $idVal = $this->buildVal($this->ids[$id]);
6915
                        if (is_array($idVal) && isset($idVal['!id'])) {
6916
                            unset($idVal['!id']);
6917
                        }
6918
                        foreach ($hrefs as $refPos => $ref) {
6919
                            $this->debug('resolving href at pos ' . $refPos);
6920
                            $this->multirefs[$id][$refPos] = $idVal;
6921
                        }
6922
                    }
6923
                }
6924
            }
6925
            xml_parser_free($this->parser);
6926
            unset($this->parser);
6927
        } else {
6928
            $this->debug('xml was empty, didn\'t parse!');
6929
            $this->setError('xml was empty, didn\'t parse!');
6930
        }
6931
    }
6932
6933
    /**
6934
     * start-element handler
6935
     *
6936
     * @param resource $parser XML parser object
6937
     * @param string   $name   element name
6938
     * @param array    $attrs  associative array of attributes
6939
     */
6940
    private function start_element($parser, $name, $attrs)
0 ignored issues
show
Unused Code introduced by
The method start_element() is not used, and could be removed.

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

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

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

6940
    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...
6941
    {
6942
        // position in a total number of elements, starting from 0
6943
        // update class level pos
6944
        $pos = $this->position++;
6945
        // and set mine
6946
        $this->message[$pos] = ['pos' => $pos, 'children' => '', 'cdata' => ''];
6947
        // depth = how many levels removed from root?
6948
        // set mine as current global depth and increment global depth value
6949
        $this->message[$pos]['depth'] = $this->depth++;
6950
6951
        // else add self as child to whoever the current parent is
6952
        if (0 != $pos) {
6953
            $this->message[$this->parent]['children'] .= '|' . $pos;
6954
        }
6955
        // set my parent
6956
        $this->message[$pos]['parent'] = $this->parent;
6957
        // set self as current parent
6958
        $this->parent = $pos;
6959
        // set self as current value for this depth
6960
        $this->depth_array[$this->depth] = $pos;
6961
        // get element prefix
6962
        if (mb_strpos($name, ':')) {
6963
            // get ns prefix
6964
            $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
6965
            // get unqualified name
6966
            $name = mb_substr(mb_strstr($name, ':'), 1);
6967
        }
6968
        // set status
6969
        if ('Envelope' === $name && '' === $this->status) {
6970
            $this->status = 'envelope';
6971
        } elseif ('Header' === $name && 'envelope' === $this->status) {
6972
            $this->root_header = $pos;
6973
            $this->status      = 'header';
6974
        } elseif ('Body' === $name && 'envelope' === $this->status) {
6975
            $this->status        = 'body';
6976
            $this->body_position = $pos;
6977
            // set method
6978
        } elseif ('body' === $this->status && $pos == ($this->body_position + 1)) {
6979
            $this->status                = 'method';
6980
            $this->root_struct_name      = $name;
6981
            $this->root_struct           = $pos;
6982
            $this->message[$pos]['type'] = 'struct';
6983
            $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6984
        }
6985
        // set my status
6986
        $this->message[$pos]['status'] = $this->status;
6987
        // set name
6988
        $this->message[$pos]['name'] = htmlspecialchars($name, ENT_QUOTES | ENT_HTML5);
6989
        // set attrs
6990
        $this->message[$pos]['attrs'] = $attrs;
6991
6992
        // loop through atts, logging ns and type declarations
6993
        $attstr = '';
6994
        foreach ($attrs as $key => $value) {
6995
            $key_prefix    = $this->getPrefix($key);
6996
            $key_localpart = $this->getLocalPart($key);
6997
            // if ns declarations, add to class level array of valid namespaces
6998
            if ('xmlns' === $key_prefix) {
6999
                if (preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) {
7000
                    $this->XMLSchemaVersion  = $value;
7001
                    $this->namespaces['xsd'] = $this->XMLSchemaVersion;
7002
                    $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
7003
                }
7004
                $this->namespaces[$key_localpart] = $value;
7005
                // set method namespace
7006
                if ($name == $this->root_struct_name) {
7007
                    $this->methodNamespace = $value;
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...
7008
                }
7009
                // if it's a type declaration, set type
7010
            } elseif ('type' === $key_localpart) {
7011
                if (isset($this->message[$pos]['type']) && 'array' === $this->message[$pos]['type']) {
7012
                    // do nothing: already processed arrayType
7013
                } else {
7014
                    $value_prefix                      = $this->getPrefix($value);
7015
                    $value_localpart                   = $this->getLocalPart($value);
7016
                    $this->message[$pos]['type']       = $value_localpart;
7017
                    $this->message[$pos]['typePrefix'] = $value_prefix;
7018
                    if (isset($this->namespaces[$value_prefix])) {
7019
                        $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
7020
                    } 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

7020
                    } elseif (isset($attrs['xmlns:' . /** @scrutinizer ignore-type */ $value_prefix])) {
Loading history...
7021
                        $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
7022
                    }
7023
                    // should do something here with the namespace of specified type?
7024
                }
7025
            } elseif ('arrayType' === $key_localpart) {
7026
                $this->message[$pos]['type'] = 'array';
7027
                /* do arrayType ereg here
7028
                [1]    arrayTypeValue    ::=    atype asize
7029
                [2]    atype    ::=    QName rank*
7030
                [3]    rank    ::=    '[' (',')* ']'
7031
                [4]    asize    ::=    '[' length~ ']'
7032
                [5]    length    ::=    nextDimension* Digit+
7033
                [6]    nextDimension    ::=    Digit+ ','
7034
                */
7035
                $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
7036
                if (preg_match($expr, $value, $regs)) {
7037
                    $this->message[$pos]['typePrefix']      = $regs[1];
7038
                    $this->message[$pos]['arrayTypePrefix'] = $regs[1];
7039
                    if (isset($this->namespaces[$regs[1]])) {
7040
                        $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
7041
                    } elseif (isset($attrs['xmlns:' . $regs[1]])) {
7042
                        $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:' . $regs[1]];
7043
                    }
7044
                    $this->message[$pos]['arrayType'] = $regs[2];
7045
                    $this->message[$pos]['arraySize'] = $regs[3];
7046
                    $this->message[$pos]['arrayCols'] = $regs[4];
7047
                }
7048
                // specifies nil value (or not)
7049
            } elseif ('nil' === $key_localpart) {
7050
                $this->message[$pos]['nil'] = ('true' === $value || '1' == $value);
7051
                // some other attribute
7052
            } elseif ('href' !== $key && 'xmlns' !== $key && 'encodingStyle' !== $key_localpart && 'root' !== $key_localpart) {
7053
                $this->message[$pos]['xattrs']['!' . $key] = $value;
7054
            }
7055
7056
            if ('xmlns' === $key) {
7057
                $this->default_namespace = $value;
7058
            }
7059
            // log id
7060
            if ('id' === $key) {
7061
                $this->ids[$value] = $pos;
7062
            }
7063
            // root
7064
            if ('root' === $key_localpart && 1 == $value) {
7065
                $this->status           = 'method';
7066
                $this->root_struct_name = $name;
7067
                $this->root_struct      = $pos;
7068
                $this->debug("found root struct $this->root_struct_name, pos $pos");
7069
            }
7070
            // for doclit
7071
            $attstr .= " $key=\"$value\"";
7072
        }
7073
        // get namespace - must be done after namespace atts are processed
7074
        if (isset($prefix)) {
7075
            $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
7076
            $this->default_namespace          = $this->namespaces[$prefix];
7077
        } else {
7078
            $this->message[$pos]['namespace'] = $this->default_namespace;
7079
        }
7080
        if ('header' === $this->status) {
7081
            if ($this->root_header != $pos) {
7082
                $this->responseHeaders .= '<' . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
7083
            }
7084
        } elseif ('' !== $this->root_struct_name) {
7085
            $this->document .= '<' . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
7086
        }
7087
    }
7088
7089
    /**
7090
     * end-element handler
7091
     *
7092
     * @param resource $parser XML parser object
7093
     * @param string   $name   element name
7094
     */
7095
    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

7095
    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...
7096
    {
7097
        // position of current element is equal to the last value left in depth_array for my depth
7098
        $pos = $this->depth_array[$this->depth--];
7099
7100
        // get element prefix
7101
        if (mb_strpos($name, ':')) {
7102
            // get ns prefix
7103
            $prefix = mb_substr($name, 0, mb_strpos($name, ':'));
7104
            // get unqualified name
7105
            $name = mb_substr(mb_strstr($name, ':'), 1);
7106
        }
7107
7108
        // build to native type
7109
        if (isset($this->body_position) && $pos > $this->body_position) {
7110
            // deal w/ multirefs
7111
            if (isset($this->message[$pos]['attrs']['href'])) {
7112
                // get id
7113
                $id = mb_substr($this->message[$pos]['attrs']['href'], 1);
7114
                // add placeholder to href array
7115
                $this->multirefs[$id][$pos] = 'placeholder';
7116
                // add set a reference to it as the result value
7117
                $this->message[$pos]['result'] = $this->multirefs[$id][$pos];
7118
                // build complexType values
7119
            } elseif ('' !== $this->message[$pos]['children']) {
7120
                // if result has already been generated (struct/array)
7121
                if (!isset($this->message[$pos]['result'])) {
7122
                    $this->message[$pos]['result'] = $this->buildVal($pos);
7123
                }
7124
                // build complexType values of attributes and possibly simpleContent
7125
            } elseif (isset($this->message[$pos]['xattrs'])) {
7126
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
7127
                    $this->message[$pos]['xattrs']['!'] = null;
7128
                } elseif (isset($this->message[$pos]['cdata']) && '' !== trim($this->message[$pos]['cdata'])) {
7129
                    if (isset($this->message[$pos]['type'])) {
7130
                        $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'] : '');
7131
                    } else {
7132
                        $parent = $this->message[$pos]['parent'];
7133
                        if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7134
                            $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7135
                        } else {
7136
                            $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
7137
                        }
7138
                    }
7139
                }
7140
                $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
7141
                // set value of simpleType (or nil complexType)
7142
            } else {
7143
                //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
7144
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
7145
                    $this->message[$pos]['xattrs']['!'] = null;
7146
                } elseif (isset($this->message[$pos]['type'])) {
7147
                    $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'] : '');
7148
                } else {
7149
                    $parent = $this->message[$pos]['parent'];
7150
                    if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7151
                        $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7152
                    } else {
7153
                        $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
7154
                    }
7155
                }
7156
7157
                /* add value to parent's result, if parent is struct/array
7158
                $parent = $this->message[$pos]['parent'];
7159
                if ($this->message[$parent]['type'] != 'map') {
7160
                    if (strtolower($this->message[$parent]['type']) == 'array') {
7161
                        $this->message[$parent]['result'][] = $this->message[$pos]['result'];
7162
                    } else {
7163
                        $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
7164
                    }
7165
                }
7166
                */
7167
            }
7168
        }
7169
7170
        // for doclit
7171
        if ('header' === $this->status) {
7172
            if ($this->root_header != $pos) {
7173
                $this->responseHeaders .= '</' . (isset($prefix) ? $prefix . ':' : '') . "$name>";
7174
            }
7175
        } elseif ($pos >= $this->root_struct) {
7176
            $this->document .= '</' . (isset($prefix) ? $prefix . ':' : '') . "$name>";
7177
        }
7178
        // switch status
7179
        if ($pos == $this->root_struct) {
7180
            $this->status                = 'body';
7181
            $this->root_struct_namespace = $this->message[$pos]['namespace'];
7182
        } elseif ($pos == $this->root_header) {
7183
            $this->status = 'envelope';
7184
        } elseif ('Body' === $name && 'body' === $this->status) {
7185
            $this->status = 'envelope';
7186
        } elseif ('Header' === $name && 'header' === $this->status) {
7187
            // will never happen
7188
            $this->status = 'envelope';
7189
        } elseif ('Envelope' === $name && 'envelope' === $this->status) {
7190
            $this->status = '';
7191
        }
7192
        // set parent back to my parent
7193
        $this->parent = $this->message[$pos]['parent'];
7194
    }
7195
7196
    /**
7197
     * element content handler
7198
     *
7199
     * @param resource $parser XML parser object
7200
     * @param string   $data   element content
7201
     */
7202
    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

7202
    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...
7203
    {
7204
        $pos = $this->depth_array[$this->depth];
7205
        if ('UTF-8' === $this->xml_encoding) {
7206
            // TODO: add an option to disable this for folks who want
7207
            // raw UTF-8 that, e.g., might not map to iso-8859-1
7208
            // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
7209
            if ($this->decode_utf8) {
7210
                $data = utf8_decode($data);
7211
            }
7212
        }
7213
        $this->message[$pos]['cdata'] .= $data;
7214
        // for doclit
7215
        if ('header' === $this->status) {
7216
            $this->responseHeaders .= $data;
7217
        } else {
7218
            $this->document .= $data;
7219
        }
7220
    }
7221
7222
    /**
7223
     * get the parsed message (SOAP Body)
7224
     *
7225
     * @return mixed
7226
     * @deprecated   use get_soapbody instead
7227
     */
7228
    public function get_response()
7229
    {
7230
        return $this->soapresponse;
7231
    }
7232
7233
    /**
7234
     * get the parsed SOAP Body (null if there was none)
7235
     *
7236
     * @return mixed
7237
     */
7238
    public function get_soapbody()
7239
    {
7240
        return $this->soapresponse;
7241
    }
7242
7243
    /**
7244
     * get the parsed SOAP Header (null if there was none)
7245
     *
7246
     * @return mixed
7247
     */
7248
    public function get_soapheader()
7249
    {
7250
        return $this->soapheader;
7251
    }
7252
7253
    /**
7254
     * get the unparsed SOAP Header
7255
     *
7256
     * @return string XML or empty if no Header
7257
     */
7258
    public function getHeaders()
7259
    {
7260
        return $this->responseHeaders;
7261
    }
7262
7263
    /**
7264
     * decodes simple types into PHP variables
7265
     *
7266
     * @param  string $value  value to decode
7267
     * @param  string $type   XML type to decode
7268
     * @param  string $typens XML type namespace to decode
7269
     * @return mixed  PHP value
7270
     */
7271
    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

7271
    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...
7272
    {
7273
        // TODO: use the namespace!
7274
        if (!isset($type) || 'string' === $type || 'long' === $type || 'unsignedLong' === $type) {
7275
            return (string)$value;
7276
        }
7277
        if ('int' === $type || 'integer' === $type || 'short' === $type || 'byte' === $type) {
7278
            return (int)$value;
7279
        }
7280
        if ('float' === $type || 'double' === $type || 'decimal' === $type) {
7281
            return (float)$value;
7282
        }
7283
        if ('boolean' === $type) {
7284
            if ('false' === mb_strtolower($value) || 'f' === mb_strtolower($value)) {
7285
                return false;
7286
            }
7287
7288
            return (bool)$value;
7289
        }
7290
        if ('base64' === $type || 'base64Binary' === $type) {
7291
            $this->debug('Decode base64 value');
7292
7293
            return base64_decode($value, true);
7294
        }
7295
        // obscure numeric types
7296
        if ('nonPositiveInteger' === $type || 'negativeInteger' === $type || 'nonNegativeInteger' === $type || 'positiveInteger' === $type || 'unsignedInt' === $type || 'unsignedShort' === $type || 'unsignedByte' === $type) {
7297
            return (int)$value;
7298
        }
7299
        // bogus: parser treats array with no elements as a simple type
7300
        if ('array' === $type) {
7301
            return [];
7302
        }
7303
7304
        // everything else
7305
        return (string)$value;
7306
    }
7307
7308
    /**
7309
     * builds response structures for compound values (arrays/structs)
7310
     * and scalars
7311
     *
7312
     * @param int $pos position in node tree
7313
     * @return mixed   PHP value
7314
     */
7315
    private function buildVal($pos)
7316
    {
7317
        if (!isset($this->message[$pos]['type'])) {
7318
            $this->message[$pos]['type'] = '';
7319
        }
7320
        $this->debug('in buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
7321
        // if there are children...
7322
        if ('' !== $this->message[$pos]['children']) {
7323
            $this->debug('in buildVal, there are children');
7324
            $children = explode('|', $this->message[$pos]['children']);
7325
            array_shift($children); // knock off empty
7326
            // md array
7327
            if (isset($this->message[$pos]['arrayCols']) && '' !== $this->message[$pos]['arrayCols']) {
7328
                $r = 0; // rowcount
7329
                $c = 0; // colcount
7330
                foreach ($children as $child_pos) {
7331
                    $this->debug("in buildVal, got an MD array element: $r, $c");
7332
                    $params[$r][] = $this->message[$child_pos]['result'];
7333
                    ++$c;
7334
                    if ($c == $this->message[$pos]['arrayCols']) {
7335
                        $c = 0;
7336
                        ++$r;
7337
                    }
7338
                }
7339
                // array
7340
            } elseif ('array' === $this->message[$pos]['type'] || 'Array' === $this->message[$pos]['type']) {
7341
                $this->debug('in buildVal, adding array ' . $this->message[$pos]['name']);
7342
                foreach ($children as $child_pos) {
7343
                    $params[] = $this->message[$child_pos]['result'];
7344
                }
7345
                // apache Map type: java hashtable
7346
            } elseif ('Map' === $this->message[$pos]['type'] && 'http://xml.apache.org/xml-soap' === $this->message[$pos]['type_namespace']) {
7347
                $this->debug('in buildVal, Java Map ' . $this->message[$pos]['name']);
7348
                foreach ($children as $child_pos) {
7349
                    $kv                                       = explode('|', $this->message[$child_pos]['children']);
7350
                    $params[$this->message[$kv[1]]['result']] = $this->message[$kv[2]]['result'];
7351
                }
7352
                // generic compound type
7353
                //} elseif ($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
7354
            } else {
7355
                // Apache Vector type: treat as an array
7356
                $this->debug('in buildVal, adding Java Vector or generic compound type ' . $this->message[$pos]['name']);
7357
                if ('Vector' === $this->message[$pos]['type'] && 'http://xml.apache.org/xml-soap' === $this->message[$pos]['type_namespace']) {
7358
                    $notstruct = 1;
7359
                } else {
7360
                    $notstruct = 0;
7361
                }
7362
7363
                foreach ($children as $child_pos) {
7364
                    if ($notstruct) {
7365
                        $params[] = &$this->message[$child_pos]['result'];
7366
                    } else {
7367
                        if (isset($params[$this->message[$child_pos]['name']])) {
7368
                            // de-serialize repeated element name into an array
7369
                            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 7365. Are you sure it is defined here?
Loading history...
7370
                                $params[$this->message[$child_pos]['name']] = [$params[$this->message[$child_pos]['name']]];
7371
                            }
7372
                            $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
7373
                        } else {
7374
                            $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
7375
                        }
7376
                    }
7377
                }
7378
            }
7379
            if (isset($this->message[$pos]['xattrs'])) {
7380
                $this->debug('in buildVal, handling attributes');
7381
                foreach ($this->message[$pos]['xattrs'] as $n => $v) {
7382
                    $params[$n] = $v;
7383
                }
7384
            }
7385
            // handle simpleContent
7386
            if (isset($this->message[$pos]['cdata']) && '' !== trim($this->message[$pos]['cdata'])) {
7387
                $this->debug('in buildVal, handling simpleContent');
7388
                if (isset($this->message[$pos]['type'])) {
7389
                    $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7390
                } else {
7391
                    $parent = $this->message[$pos]['parent'];
7392
                    if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7393
                        $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7394
                    } else {
7395
                        $params['!'] = $this->message[$pos]['cdata'];
7396
                    }
7397
                }
7398
            }
7399
            $ret = is_array($params) ? $params : [];
7400
            $this->debug('in buildVal, return:');
7401
            $this->appendDebug($this->varDump($ret));
7402
7403
            return $ret;
7404
        }
7405
7406
        $this->debug('in buildVal, no children, building scalar');
7407
        $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
7408
        if (isset($this->message[$pos]['type'])) {
7409
            $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7410
            $this->debug("in buildVal, return: $ret");
7411
7412
            return $ret;
7413
        }
7414
        $parent = $this->message[$pos]['parent'];
7415
        if (isset($this->message[$parent]['type']) && ('array' === $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7416
            $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7417
            $this->debug("in buildVal, return: $ret");
7418
7419
            return $ret;
7420
        }
7421
        $ret = $this->message[$pos]['cdata'];
7422
        $this->debug("in buildVal, return: $ret");
7423
7424
        return $ret;
7425
    }
7426
}
7427
7428
/**
7429
 * Backward compatibility
7430
 */
7431
class Soap_parser extends Nusoap_parser
7432
{
7433
}
7434
7435
/**
7436
 * [nu]soapclient higher level class for easy usage.
7437
 *
7438
 * usage:
7439
 *
7440
 * // instantiate client with server info
7441
 * $soapclient = new Nusoap_client( string path [ ,mixed wsdl] );
7442
 *
7443
 * // call method, get results
7444
 * echo $soapclient->call( string methodname [ ,array parameters] );
7445
 *
7446
 * // bye bye client
7447
 * unset($soapclient);
7448
 *
7449
 * @author   Dietrich Ayala <[email protected]>
7450
 * @author   Scott Nichol <[email protected]>
7451
 */
7452
class Nusoap_client extends Nusoap_base
7453
{
7454
    public $username             = '';                // Username for HTTP authentication
7455
    public $password             = '';                // Password for HTTP authentication
7456
    public $authtype             = '';                // Type of HTTP authentication
7457
    public $certRequest          = [];        // Certificate for HTTP SSL authentication
7458
    public $requestHeaders       = false;    // SOAP headers in request (text)
7459
    public $responseHeaders      = '';        // SOAP headers from response (incomplete namespace resolution) (text)
7460
    public $responseHeader;        // SOAP Header from response (parsed)
7461
    public $document             = '';                // SOAP body response portion (incomplete namespace resolution) (text)
7462
    public $endpoint;
7463
    public $forceEndpoint        = '';        // overrides WSDL endpoint
7464
    public $proxyhost            = '';
7465
    public $proxyport            = '';
7466
    public $proxyusername        = '';
7467
    public $proxypassword        = '';
7468
    public $portName             = '';                // port name to use in WSDL
7469
    public $xml_encoding         = '';            // character set encoding of incoming (response) messages
7470
    public $http_encoding        = false;
7471
    public $timeout              = 0;                // HTTP connection timeout
7472
    public $response_timeout     = 30;        // HTTP response timeout
7473
    public $endpointType         = '';            // soap|wsdl, empty for WSDL initialization error
7474
    public $persistentConnection = false;
7475
    public $defaultRpcParams     = false;    // This is no longer used
7476
    public $request              = '';                // HTTP request
7477
    public $response             = '';                // HTTP response
7478
    public $responseData         = '';            // SOAP payload of response
7479
    public $cookies              = [];            // Cookies from response or for request
7480
    public $decode_utf8          = true;        // toggles whether the parser decodes element content w/ utf8_decode()
7481
    public $operations           = [];        // WSDL operations, empty for WSDL initialization error
7482
    public $curl_options         = [];    // User-specified cURL options
7483
    public $bindingType          = '';            // WSDL operation binding type
7484
    public $use_curl             = false;            // whether to always try to use cURL
7485
7486
    /*
7487
     * fault related variables
7488
     */
7489
    /**
7490
     * @var  string|bool $fault
7491
     */
7492
    public $fault;
7493
    /**
7494
     * @var  string $faultcode
7495
     */
7496
    public $faultcode;
7497
    /**
7498
     * @var string $faultstring
7499
     */
7500
    public $faultstring;
7501
    /**
7502
     * @var string $faultdetail
7503
     */
7504
    public $faultdetail;
7505
7506
    /**
7507
     * constructor
7508
     *
7509
     * @param mixed       $endpoint         SOAP server or WSDL URL (string) , or wsdl instance (object)
7510
     * @param mixed       $wsdl             optional, set to 'wsdl' or true if using WSDL
7511
     * @param bool|string $proxyhost        optional
7512
     * @param bool|string $proxyport        optional
7513
     * @param bool|string $proxyusername    optional
7514
     * @param bool|string $proxypassword    optional
7515
     * @param int         $timeout          set the connection timeout
7516
     * @param int         $response_timeout set the response timeout
7517
     * @param string      $portName         optional portName in WSDL document
7518
     */
7519
    public function __construct(
7520
        $endpoint,
7521
        $wsdl = false,
7522
        $proxyhost = false,
7523
        $proxyport = false,
7524
        $proxyusername = false,
7525
        $proxypassword = false,
7526
        $timeout = 0,
7527
        $response_timeout = 30,
7528
        $portName = '')
7529
    {
7530
        parent::__construct();
7531
        $this->endpoint         = $endpoint;
7532
        $this->proxyhost        = $proxyhost;
7533
        $this->proxyport        = $proxyport;
7534
        $this->proxyusername    = $proxyusername;
7535
        $this->proxypassword    = $proxypassword;
7536
        $this->timeout          = $timeout;
7537
        $this->response_timeout = $response_timeout;
7538
        $this->portName         = $portName;
7539
7540
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7541
        $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7542
7543
        // make values
7544
        if ($wsdl) {
7545
            if (is_object($endpoint) && ('Wsdl' === get_class($endpoint))) {
7546
                $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...
7547
                $this->endpoint = $this->wsdl->wsdl;
7548
                $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...
7549
                $this->debug('existing wsdl instance created from ' . $this->endpoint);
7550
                $this->checkWSDL();
7551
            } else {
7552
                $this->wsdlFile = $this->endpoint;
7553
                $this->wsdl     = null;
7554
                $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
7555
            }
7556
            $this->endpointType = 'wsdl';
7557
        } else {
7558
            $this->debug("instantiate SOAP with endpoint at $endpoint");
7559
            $this->endpointType = 'soap';
7560
        }
7561
    }
7562
7563
    /**
7564
     * calls method, returns PHP native type
7565
     *
7566
     * @param  string $operation   SOAP server URL or path
7567
     * @param  mixed  $params      An array, associative or simple, of the parameters
7568
     *                             for the method call, or a string that is the XML
7569
     *                             for the call.  For rpc style, this call will
7570
     *                             wrap the XML in a tag named after the method, as
7571
     *                             well as the SOAP Envelope and Body.  For document
7572
     *                             style, this will only wrap with the Envelope and Body.
7573
     *                             IMPORTANT: when using an array with document style,
7574
     *                             in which case there
7575
     *                             is really one parameter, the root of the fragment
7576
     *                             used in the call, which encloses what programmers
7577
     *                             normally think of parameters.  A parameter array
7578
     *                             *must* include the wrapper.
7579
     * @param  string $namespace   optional method namespace (WSDL can override)
7580
     * @param  string $soapAction  optional SOAPAction value (WSDL can override)
7581
     * @param  mixed  $headers     optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7582
     * @param  bool   $rpcParams   optional (no longer used)
7583
     * @param  string $style       optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7584
     * @param  string $use         optional (encoded|literal) the use when serializing parameters (WSDL can override)
7585
     * @return mixed   response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
7586
     */
7587
    public function call(
7588
        $operation,
7589
        $params = [],
7590
        $namespace = 'http://tempuri.org',
7591
        $soapAction = '',
7592
        $headers = false,
7593
        $rpcParams = null,
7594
        $style = 'rpc',
7595
        $use = 'encoded')
7596
    {
7597
        $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...
7598
        $this->fault     = false;
7599
        $this->setError('');
7600
        $this->request      = '';
7601
        $this->response     = '';
7602
        $this->responseData = '';
7603
        $this->faultstring  = '';
7604
        $this->faultcode    = '';
7605
        $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...
7606
        $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7607
        $this->appendDebug('params=' . $this->varDump($params));
7608
        $this->appendDebug('headers=' . $this->varDump($headers));
7609
        if ($headers) {
7610
            $this->requestHeaders = $headers;
7611
        }
7612
        if ('Wsdl' === $this->endpointType && null === $this->wsdl) {
7613
            $this->loadWSDL();
7614
            if ($this->getError()) {
7615
                return false;
7616
            }
7617
        }
7618
        // serialize parameters
7619
        if ('Wsdl' === $this->endpointType && $opData = $this->getOperationData($operation)) {
7620
            // use WSDL for operation
7621
            $this->opData = $opData;
7622
            $this->debug('found operation');
7623
            $this->appendDebug('opData=' . $this->varDump($opData));
7624
            if (isset($opData['soapAction'])) {
7625
                $soapAction = $opData['soapAction'];
7626
            }
7627
            if (!$this->forceEndpoint) {
7628
                $this->endpoint = $opData['endpoint'];
7629
            } else {
7630
                $this->endpoint = $this->forceEndpoint;
7631
            }
7632
            $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7633
            $style     = $opData['style'];
7634
            $use       = $opData['input']['use'];
7635
            // add ns to ns array
7636
            if ('' !== $namespace && !isset($this->wsdl->namespaces[$namespace])) {
7637
                $nsPrefix                          = 'ns' . mt_rand(1000, 9999);
7638
                $this->wsdl->namespaces[$nsPrefix] = $namespace;
7639
            }
7640
            $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7641
            // serialize payload
7642
            if (is_string($params)) {
7643
                $this->debug("serializing param string for WSDL operation $operation");
7644
                $payload = $params;
7645
            } elseif (is_array($params)) {
7646
                $this->debug("serializing param array for WSDL operation $operation");
7647
                $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params, $this->bindingType);
7648
            } else {
7649
                $this->debug('params must be array or string');
7650
                $this->setError('params must be array or string');
7651
7652
                return false;
7653
            }
7654
            $usedNamespaces = $this->wsdl->usedNamespaces;
7655
            $encodingStyle  = '';
7656
            if (isset($opData['input']['encodingStyle'])) {
7657
                $encodingStyle = $opData['input']['encodingStyle'];
7658
            }
7659
            $this->appendDebug($this->wsdl->getDebug());
7660
            $this->wsdl->clearDebug();
7661
            if (false !== ($errstr = $this->wsdl->getError())) {
7662
                $this->debug('got wsdl error: ' . $errstr);
7663
                $this->setError('wsdl error: ' . $errstr);
7664
7665
                return false;
7666
            }
7667
        } elseif ('Wsdl' === $this->endpointType) {
7668
            // operation not in WSDL
7669
            $this->appendDebug($this->wsdl->getDebug());
7670
            $this->wsdl->clearDebug();
7671
            $this->setError('operation ' . $operation . ' not present in WSDL.');
7672
            $this->debug("operation '$operation' not present in WSDL.");
7673
7674
            return false;
7675
        } else {
7676
            // no WSDL
7677
            //$this->namespaces['ns1'] = $namespace;
7678
            $nsPrefix = 'ns' . mt_rand(1000, 9999);
7679
            // serialize
7680
            $payload = '';
7681
            if (is_string($params)) {
7682
                $this->debug("serializing param string for operation $operation");
7683
                $payload = $params;
7684
            } elseif (is_array($params)) {
7685
                $this->debug("serializing param array for operation $operation");
7686
                foreach ($params as $k => $v) {
7687
                    $payload .= $this->serialize_val($v, $k, false, false, false, false, $use);
7688
                }
7689
            } else {
7690
                $this->debug('params must be array or string');
7691
                $this->setError('params must be array or string');
7692
7693
                return false;
7694
            }
7695
            $usedNamespaces = [];
7696
            $encodingStyle  = '';
7697
            if ('encoded' === $use) {
7698
                $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7699
            }
7700
        }
7701
        // wrap RPC calls with method element
7702
        if ('rpc' === $style) {
7703
            if ('literal' === $use) {
7704
                $this->debug('wrapping RPC request with literal method element');
7705
                if ($namespace) {
7706
                    // 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
7707
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . $payload . "</$nsPrefix:$operation>";
7708
                } else {
7709
                    $payload = "<$operation>" . $payload . "</$operation>";
7710
                }
7711
            } else {
7712
                $this->debug('wrapping RPC request with encoded method element');
7713
                if ($namespace) {
7714
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . $payload . "</$nsPrefix:$operation>";
7715
                } else {
7716
                    $payload = "<$operation>" . $payload . "</$operation>";
7717
                }
7718
            }
7719
        }
7720
        // serialize envelope
7721
        $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $usedNamespaces, $style, $use, $encodingStyle);
7722
        $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7723
        $this->debug('SOAP message length=' . mb_strlen($soapmsg) . ' contents (max 1000 bytes)=' . mb_substr($soapmsg, 0, 1000));
7724
        // send
7725
        $return = $this->send($this->getHTTPBody($soapmsg), $soapAction, $this->timeout, $this->response_timeout);
7726
        if (false !== ($errstr = $this->getError())) {
7727
            $this->debug('Error: ' . $errstr);
7728
7729
            return false;
7730
        }
7731
        $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...
7732
        $this->debug('sent message successfully and got a(n) ' . gettype($return));
7733
        $this->appendDebug('return=' . $this->varDump($return));
7734
        // fault?
7735
        if (is_array($return) && isset($return['faultcode'])) {
7736
            $this->debug('got fault');
7737
            $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
7738
            $this->fault = true;
7739
            foreach ($return as $k => $v) {
7740
                $this->$k = $v;
7741
                if (is_array($v)) {
7742
                    $this->debug("$k = " . json_encode($v));
7743
                } else {
7744
                    $this->debug("$k = $v<br>");
7745
                }
7746
            }
7747
7748
            return $return;
7749
        } elseif ('document' === $style) {
7750
            // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7751
            // we are only going to return the first part here...sorry about that
7752
            return $return;
7753
        }
7754
        // array of return values
7755
        if (is_array($return)) {
7756
            // multiple 'out' parameters, which we return wrapped up
7757
            // in the array
7758
            if (is_array($return) && count($return) > 1) {
7759
                return $return;
7760
            }
7761
            // single 'out' parameter (normally the return value)
7762
            $return = array_shift($return);
7763
            $this->debug('return shifted value: ');
7764
            $this->appendDebug($this->varDump($return));
7765
7766
            return $return;
7767
            // nothing returned (ie, echoVoid)
7768
        }
7769
7770
        return '';
7771
    }
7772
7773
    /**
7774
     * Check WSDL passed as an instance or pulled from an endpoint
7775
     */
7776
    private function checkWSDL()
7777
    {
7778
        $this->appendDebug($this->wsdl->getDebug());
7779
        $this->wsdl->clearDebug();
7780
        $this->debug('checkWSDL');
7781
        // catch errors
7782
        if (false !== ($errstr = $this->wsdl->getError())) {
7783
            $this->appendDebug($this->wsdl->getDebug());
7784
            $this->wsdl->clearDebug();
7785
            $this->debug('got wsdl error: ' . $errstr);
7786
            $this->setError('wsdl error: ' . $errstr);
7787
        } elseif (false !== ($this->operations = $this->wsdl->getOperations($this->portName, 'soap'))) {
7788
            $this->appendDebug($this->wsdl->getDebug());
7789
            $this->wsdl->clearDebug();
7790
            $this->bindingType = 'soap';
7791
            $this->debug('got ' . count($this->operations) . ' operations from Wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7792
        } elseif (false !== ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12'))) {
7793
            $this->appendDebug($this->wsdl->getDebug());
7794
            $this->wsdl->clearDebug();
7795
            $this->bindingType = 'soap12';
7796
            $this->debug('got ' . count($this->operations) . ' operations from Wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7797
            $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7798
        } else {
7799
            $this->appendDebug($this->wsdl->getDebug());
7800
            $this->wsdl->clearDebug();
7801
            $this->debug('getOperations returned false');
7802
            $this->setError('no operations defined in the WSDL document!');
7803
        }
7804
    }
7805
7806
    /**
7807
     * Instantiate Wsdl object and parse Wsdl file
7808
     */
7809
    public function loadWSDL()
7810
    {
7811
        $this->debug('instantiating Wsdl class with doc: ' . $this->wsdlFile);
7812
        $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...
7813
        $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7814
        $this->wsdl->fetchWSDL($this->wsdlFile);
7815
        $this->checkWSDL();
7816
    }
7817
7818
    /**
7819
     * Get available data pertaining to an operation
7820
     *
7821
     * @param  string $operation operation name
7822
     * @return   bool|array array of data pertaining to the operation
7823
     */
7824
    public function getOperationData($operation)
7825
    {
7826
        if ('Wsdl' === $this->endpointType && null === $this->wsdl) {
7827
            $this->loadWSDL();
7828
            if ($this->getError()) {
7829
                return false;
7830
            }
7831
        }
7832
        if (isset($this->operations[$operation])) {
7833
            return $this->operations[$operation];
7834
        }
7835
        $this->debug("No data for operation: $operation");
7836
    }
7837
7838
    /**
7839
     * Send the SOAP message
7840
     *
7841
     * Note: if the operation has multiple return values
7842
     * the return value of this method will be an array
7843
     * of those values.
7844
     *
7845
     * @param  string $msg              a SOAPx4 soapmsg object
7846
     * @param  string $soapaction       SOAPAction value
7847
     * @param int     $timeout          set connection timeout in seconds
7848
     * @param int     $response_timeout set response timeout in seconds
7849
     * @return mixed   native PHP types.
7850
     */
7851
    private function send($msg, $soapaction = '', $timeout = 0, $response_timeout = 30)
7852
    {
7853
        $this->checkCookies();
7854
        // detect transport
7855
        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...
7856
            // http(s)
7857
            case preg_match('/^http/', $this->endpoint):
7858
                $this->debug('transporting via HTTP');
7859
                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...
7860
                    $http = $this->persistentConnection;
7861
                } else {
7862
                    $http = new Soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7863
                    if ($this->persistentConnection) {
7864
                        $http->usePersistentConnection();
7865
                    }
7866
                }
7867
                $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7868
                $http->setSOAPAction($soapaction);
7869
                if ($this->proxyhost && $this->proxyport) {
7870
                    $http->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
7871
                }
7872
                if ('' !== $this->authtype) {
7873
                    $http->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
7874
                }
7875
                if ('' !== $this->http_encoding) {
0 ignored issues
show
introduced by
The condition '' !== $this->http_encoding is always true.
Loading history...
7876
                    $http->setEncoding($this->http_encoding);
7877
                }
7878
                $this->debug('sending message, length=' . mb_strlen($msg));
7879
                if (preg_match('/^http:/', $this->endpoint)) {
7880
                    //if (strpos($this->endpoint,'http:')) {
7881
                    $this->responseData = $http->send($msg, $timeout, $response_timeout, $this->cookies);
7882
                } elseif (preg_match('/^https/', $this->endpoint)) {
7883
                    //} elseif (strpos($this->endpoint,'https:')) {
7884
                    //if (PHP_VERSION == '4.3.0-dev') {
7885
                    //$response = $http->send($msg,$timeout,$response_timeout);
7886
                    //$this->request = $http->outgoing_payload;
7887
                    //$this->response = $http->incoming_payload;
7888
                    //} else
7889
                    $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

7889
                    $this->responseData = /** @scrutinizer ignore-deprecated */ $http->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
Loading history...
7890
                } else {
7891
                    $this->setError('no http/s in endpoint url');
7892
                }
7893
                $this->request  = $http->outgoing_payload;
7894
                $this->response = $http->incoming_payload;
7895
                $this->appendDebug($http->getDebug());
7896
                $this->UpdateCookies($http->incoming_cookies);
7897
                // save transport object if using persistent connections
7898
                if ($this->persistentConnection) {
7899
                    $http->clearDebug();
7900
                    if (!is_object($this->persistentConnection)) {
0 ignored issues
show
introduced by
The condition is_object($this->persistentConnection) is always false.
Loading history...
7901
                        $this->persistentConnection = $http;
7902
                    }
7903
                }
7904
7905
                if (false !== ($err = $http->getError())) {
7906
                    $this->setError('HTTP Error: ' . $err);
7907
7908
                    return false;
7909
                } elseif ($this->getError()) {
7910
                    return false;
7911
                }
7912
                $this->debug('got response, length=' . mb_strlen($this->responseData) . ' type=' . $http->incoming_headers['content-type']);
7913
7914
                return $this->parseResponse($http->incoming_headers, $this->responseData);
7915
                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...
7916
            default:
7917
                $this->setError('no transport found, or selected transport is not yet supported!');
7918
7919
                return false;
7920
                break;
7921
        }
7922
    }
7923
7924
    /**
7925
     * Processes SOAP message returned from server
7926
     *
7927
     * @param  array  $headers The HTTP headers
7928
     * @param  string $data    unprocessed response data from server
7929
     * @return mixed  value of the message, decoded into a PHP type
7930
     */
7931
    private function parseResponse($headers, $data)
7932
    {
7933
        $this->debug('Entering parseResponse() for data of length ' . mb_strlen($data) . ' headers:');
7934
        $this->appendDebug($this->varDump($headers));
7935
        if (!isset($headers['content-type'])) {
7936
            $this->setError('Response not of type text/xml (no content-type header)');
7937
7938
            return false;
7939
        }
7940
        if (false === mb_strpos($headers['content-type'], 'text/xml')) {
7941
            $this->setError('Response not of type text/xml: ' . $headers['content-type']);
7942
7943
            return false;
7944
        }
7945
        if (mb_strpos($headers['content-type'], '=')) {
7946
            $enc = str_replace('"', '', mb_substr(mb_strstr($headers['content-type'], '='), 1));
7947
            $this->debug('Got response encoding: ' . $enc);
7948
            $this->xml_encoding = 'US-ASCII';
7949
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
7950
                $this->xml_encoding = mb_strtoupper($enc);
7951
            }
7952
        } else {
7953
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
7954
            $this->xml_encoding = 'ISO-8859-1';
7955
        }
7956
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating Nusoap_parser');
7957
        $parser = new Nusoap_parser($data, $this->xml_encoding, $this->operations, $this->decode_utf8);
7958
        // add parser debug data to our debug
7959
        $this->appendDebug($parser->getDebug());
7960
        // if parse errors
7961
        if (false !== ($errstr = $parser->getError())) {
7962
            $this->setError($errstr);
7963
            // destroy the parser object
7964
            unset($parser);
7965
7966
            return false;
7967
        }
7968
        // get SOAP headers
7969
        $this->responseHeaders = $parser->getHeaders();
7970
        // get SOAP headers
7971
        $this->responseHeader = $parser->get_soapheader();
7972
        // get decoded message
7973
        $return = $parser->get_soapbody();
7974
        // add document for doclit support
7975
        $this->document = $parser->document;
7976
        // destroy the parser object
7977
        unset($parser);
7978
        // return decode message
7979
        return $return;
7980
    }
7981
7982
    /**
7983
     * Sets user-specified cURL options
7984
     *
7985
     * @param mixed $option The cURL option (always integer?)
7986
     * @param mixed $value  The cURL option value
7987
     */
7988
    public function setCurlOption($option, $value)
7989
    {
7990
        $this->debug("setCurlOption option=$option, value=");
7991
        $this->appendDebug($this->varDump($value));
7992
        $this->curl_options[$option] = $value;
7993
    }
7994
7995
    /**
7996
     * Sets the SOAP endpoint, which can override WSDL
7997
     *
7998
     * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override
7999
     */
8000
    public function setEndpoint($endpoint)
8001
    {
8002
        $this->debug("setEndpoint(\"$endpoint\")");
8003
        $this->forceEndpoint = $endpoint;
8004
    }
8005
8006
    /**
8007
     * Set the SOAP headers
8008
     *
8009
     * @param    mixed $headers String of XML with SOAP header content, or array of Soapval objects for SOAP headers
8010
     */
8011
    public function setHeaders($headers)
8012
    {
8013
        $this->debug('setHeaders headers=');
8014
        $this->appendDebug($this->varDump($headers));
8015
        $this->requestHeaders = $headers;
8016
    }
8017
8018
    /**
8019
     * Get the SOAP response headers (namespace resolution incomplete)
8020
     *
8021
     * @return string
8022
     */
8023
    public function getHeaders()
8024
    {
8025
        return $this->responseHeaders;
8026
    }
8027
8028
    /**
8029
     * Get the SOAP response Header (parsed)
8030
     *
8031
     * @return mixed
8032
     */
8033
    public function getHeader()
8034
    {
8035
        return $this->responseHeader;
8036
    }
8037
8038
    /**
8039
     * Set proxy info here
8040
     *
8041
     * @param string $proxyhost
8042
     * @param string $proxyport
8043
     * @param string $proxyusername
8044
     * @param string $proxypassword
8045
     */
8046
    public function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
8047
    {
8048
        $this->proxyhost     = $proxyhost;
8049
        $this->proxyport     = $proxyport;
8050
        $this->proxyusername = $proxyusername;
8051
        $this->proxypassword = $proxypassword;
8052
    }
8053
8054
    /**
8055
     * If authenticating, set user credentials here
8056
     *
8057
     * @param string $username
8058
     * @param string $password
8059
     * @param string $authtype    (basic|digest|certificate|ntlm)
8060
     * @param array  $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
8061
     */
8062
    public function setCredentials($username, $password, $authtype = 'basic', $certRequest = [])
8063
    {
8064
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
8065
        $this->appendDebug($this->varDump($certRequest));
8066
        $this->username    = $username;
8067
        $this->password    = $password;
8068
        $this->authtype    = $authtype;
8069
        $this->certRequest = $certRequest;
8070
    }
8071
8072
    /**
8073
     * Use HTTP encoding
8074
     *
8075
     * @param string $enc HTTP encoding
8076
     */
8077
    public function setHTTPEncoding($enc = 'gzip, deflate')
8078
    {
8079
        $this->debug("setHTTPEncoding(\"$enc\")");
8080
        $this->http_encoding = $enc;
8081
    }
8082
8083
    /**
8084
     * Set whether to try to use cURL connections if possible
8085
     *
8086
     * @param bool $use Whether to try to use cURL
8087
     */
8088
    public function setUseCURL($use)
8089
    {
8090
        $this->debug("setUseCURL($use)");
8091
        $this->use_curl = $use;
8092
    }
8093
8094
    /**
8095
     * Use HTTP persistent connections if possible
8096
     */
8097
    public function useHTTPPersistentConnection()
8098
    {
8099
        $this->debug('useHTTPPersistentConnection');
8100
        $this->persistentConnection = true;
8101
    }
8102
8103
    /**
8104
     * Gets the default RPC parameter setting.
8105
     * If true, default is that call params are like RPC even for document style.
8106
     * Each call() can override this value.
8107
     *
8108
     * This is no longer used.
8109
     *
8110
     * @return bool
8111
     * @deprecated
8112
     */
8113
    public function getDefaultRpcParams()
8114
    {
8115
        return $this->defaultRpcParams;
8116
    }
8117
8118
    /**
8119
     * Sets the default RPC parameter setting.
8120
     * If true, default is that call params are like RPC even for document style
8121
     * Each call() can override this value.
8122
     *
8123
     * This is no longer used.
8124
     *
8125
     * @param bool $rpcParams
8126
     * @deprecated
8127
     */
8128
    public function setDefaultRpcParams($rpcParams)
8129
    {
8130
        $this->defaultRpcParams = $rpcParams;
8131
    }
8132
8133
    /**
8134
     * Dynamically creates an instance of a proxy class,
8135
     * allowing user to directly call methods from Wsdl
8136
     *
8137
     * @return object soap_proxy object
8138
     */
8139
    public function getProxy()
8140
    {
8141
        $r       = mt_rand();
8142
        $evalStr = $this->_getProxyClassCode($r);
8143
        //$this->debug("proxy class: $evalStr");
8144
        if ($this->getError()) {
8145
            $this->debug('Error from _getProxyClassCode, so return null');
8146
8147
            return null;
8148
        }
8149
        // eval the class
8150
        eval($evalStr);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
8151
        // instantiate proxy object
8152
        eval("\$proxy = new Nusoap_proxy_$r('');");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
8153
        // transfer current Wsdl data to the proxy thereby avoiding parsing the Wsdl twice
8154
        $proxy->endpointType     = 'Wsdl';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $proxy seems to be never defined.
Loading history...
8155
        $proxy->wsdlFile         = $this->wsdlFile;
8156
        $proxy->wsdl             = $this->wsdl;
8157
        $proxy->operations       = $this->operations;
8158
        $proxy->defaultRpcParams = $this->defaultRpcParams;
8159
        // transfer other state
8160
        $proxy->soap_defencoding     = $this->soap_defencoding;
8161
        $proxy->username             = $this->username;
8162
        $proxy->password             = $this->password;
8163
        $proxy->authtype             = $this->authtype;
8164
        $proxy->certRequest          = $this->certRequest;
8165
        $proxy->requestHeaders       = $this->requestHeaders;
8166
        $proxy->endpoint             = $this->endpoint;
8167
        $proxy->forceEndpoint        = $this->forceEndpoint;
8168
        $proxy->proxyhost            = $this->proxyhost;
8169
        $proxy->proxyport            = $this->proxyport;
8170
        $proxy->proxyusername        = $this->proxyusername;
8171
        $proxy->proxypassword        = $this->proxypassword;
8172
        $proxy->http_encoding        = $this->http_encoding;
8173
        $proxy->timeout              = $this->timeout;
8174
        $proxy->response_timeout     = $this->response_timeout;
8175
        $proxy->persistentConnection = $this->persistentConnection;
8176
        $proxy->decode_utf8          = $this->decode_utf8;
8177
        $proxy->curl_options         = $this->curl_options;
8178
        $proxy->bindingType          = $this->bindingType;
8179
        $proxy->use_curl             = $this->use_curl;
8180
8181
        return $proxy;
8182
    }
8183
8184
    /**
8185
     * Dynamically creates proxy class code
8186
     *
8187
     * @param string $r
8188
     * @return string PHP/NuSOAP code for the proxy class
8189
     */
8190
    private function _getProxyClassCode($r)
8191
    {
8192
        $this->debug("in getProxy endpointType=$this->endpointType");
8193
        $this->appendDebug('Wsdl=' . $this->varDump($this->wsdl));
8194
        if ('Wsdl' !== $this->endpointType) {
8195
            $evalStr = 'A proxy can only be created for a WSDL client';
8196
            $this->setError($evalStr);
8197
            $evalStr = "echo \"$evalStr\";";
8198
8199
            return $evalStr;
8200
        }
8201
        if ('Wsdl' === $this->endpointType && null === $this->wsdl) {
8202
            $this->loadWSDL();
8203
            if ($this->getError()) {
8204
                return 'echo "' . $this->getError() . '";';
8205
            }
8206
        }
8207
        $evalStr = '';
8208
        foreach ($this->operations as $operation => $opData) {
8209
            if ('' !== $operation) {
8210
                // create param string and param comment string
8211
                if (is_array($opData['input']['parts']) && count($opData['input']['parts']) > 0) {
8212
                    $paramStr        = '';
8213
                    $paramArrayStr   = '';
8214
                    $paramCommentStr = '';
8215
                    foreach ($opData['input']['parts'] as $name => $type) {
8216
                        $paramStr        .= "\$$name, ";
8217
                        $paramArrayStr   .= "'$name' => \$$name, ";
8218
                        $paramCommentStr .= "$type \$$name, ";
8219
                    }
8220
                    $paramStr        = mb_substr($paramStr, 0, -2);
8221
                    $paramArrayStr   = mb_substr($paramArrayStr, 0, -2);
8222
                    $paramCommentStr = mb_substr($paramCommentStr, 0, -2);
8223
                } else {
8224
                    $paramStr        = '';
8225
                    $paramArrayStr   = '';
8226
                    $paramCommentStr = 'void';
8227
                }
8228
                $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
8229
                $evalStr             .= "// $paramCommentStr
8230
    function " . str_replace('.', '__', $operation) . "($paramStr) {
8231
        \$params = array($paramArrayStr);
8232
        return \$this->call('$operation', \$params, '" . $opData['namespace'] . "', '" . (isset($opData['soapAction']) ? $opData['soapAction'] : '') . "');
8233
    }
8234
    ";
8235
                unset($paramStr, $paramCommentStr);
8236
            }
8237
        }
8238
        $evalStr = 'class Nusoap_proxy_' . $r . ' extends Nusoap_client {
8239
    ' . $evalStr . '
8240
}';
8241
8242
        return $evalStr;
8243
    }
8244
8245
    /**
8246
     * Dynamically creates proxy class code
8247
     *
8248
     * @return string PHP/NuSOAP code for the proxy class
8249
     */
8250
    public function getProxyClassCode()
8251
    {
8252
        $r = mt_rand();
8253
8254
        return $this->_getProxyClassCode($r);
8255
    }
8256
8257
    /**
8258
     * Gets the HTTP body for the current request.
8259
     *
8260
     * @param  string $soapmsg The SOAP payload
8261
     * @return string The HTTP body, which includes the SOAP payload
8262
     */
8263
    private function getHTTPBody($soapmsg)
8264
    {
8265
        return $soapmsg;
8266
    }
8267
8268
    /**
8269
     * Gets the HTTP content type for the current request.
8270
     *
8271
     * Note: getHTTPBody must be called before this.
8272
     *
8273
     * @return string the HTTP content type for the current request.
8274
     */
8275
    private function getHTTPContentType()
8276
    {
8277
        return 'text/xml';
8278
    }
8279
8280
    /**
8281
     * Gets the HTTP content type charset for the current request.
8282
     * Returns false for non-text content types.
8283
     *
8284
     * Note: getHTTPBody must be called before this.
8285
     *
8286
     * @return string the HTTP content type charset for the current request.
8287
     */
8288
    private function getHTTPContentTypeCharset()
8289
    {
8290
        return $this->soap_defencoding;
8291
    }
8292
8293
    /*
8294
    * Whether or not parser should decode utf8 element content
8295
    *
8296
    * @return   always returns true
8297
    * @access   public
8298
    */
8299
8300
    /**
8301
     * @param $bool
8302
     * @return bool
8303
     */
8304
    public function decodeUTF8($bool)
8305
    {
8306
        $this->decode_utf8 = $bool;
8307
8308
        return true;
8309
    }
8310
8311
    /**
8312
     * Adds a new Cookie into $this->cookies array
8313
     *
8314
     * @param  string $name  Cookie Name
8315
     * @param  string $value Cookie Value
8316
     * @return bool if cookie-set was successful returns true, else false
8317
     */
8318
    public function setCookie($name, $value)
8319
    {
8320
        if ('' === $name) {
8321
            return false;
8322
        }
8323
        $this->cookies[] = ['name' => $name, 'value' => $value];
8324
8325
        return true;
8326
    }
8327
8328
    /**
8329
     * Gets all Cookies
8330
     *
8331
     * @return array with all internal cookies
8332
     */
8333
    public function getCookies()
8334
    {
8335
        return $this->cookies;
8336
    }
8337
8338
    /**
8339
     * Checks all Cookies and delete those which are expired
8340
     *
8341
     * @return bool always return true
8342
     */
8343
    private function checkCookies()
8344
    {
8345
        if (0 === count($this->cookies)) {
8346
            return true;
8347
        }
8348
        $this->debug('checkCookie: check ' . count($this->cookies) . ' cookies');
8349
        $curr_cookies  = $this->cookies;
8350
        $this->cookies = [];
8351
        foreach ($curr_cookies as $cookie) {
8352
            if (!is_array($cookie)) {
8353
                $this->debug('Remove cookie that is not an array');
8354
                continue;
8355
            }
8356
            if (isset($cookie['expires']) && !empty($cookie['expires'])) {
8357
                if (strtotime($cookie['expires']) > time()) {
8358
                    $this->cookies[] = $cookie;
8359
                } else {
8360
                    $this->debug('Remove expired cookie ' . $cookie['name']);
8361
                }
8362
            } else {
8363
                $this->cookies[] = $cookie;
8364
            }
8365
        }
8366
        $this->debug('checkCookie: ' . count($this->cookies) . ' cookies left in array');
8367
8368
        return true;
8369
    }
8370
8371
    /**
8372
     * Updates the current cookies with a new set
8373
     *
8374
     * @param  array $cookies new cookies with which to update current ones
8375
     * @return bool always return true
8376
     */
8377
    private function UpdateCookies($cookies)
8378
    {
8379
        if (0 === count($this->cookies)) {
8380
            // no existing cookies: take whatever is new
8381
            if ($cookies && is_array($cookies)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cookies 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...
8382
                $this->debug('Setting new cookie(s)');
8383
                $this->cookies = $cookies;
8384
            }
8385
8386
            return true;
8387
        }
8388
        if (0 === count($cookies)) {
8389
            // no new cookies: keep what we've got
8390
            return true;
8391
        }
8392
        // merge
8393
        foreach ($cookies as $newCookie) {
8394
            if (!is_array($newCookie)) {
8395
                continue;
8396
            }
8397
            if (!isset($newCookie['name']) || !isset($newCookie['value'])) {
8398
                continue;
8399
            }
8400
            $newName = $newCookie['name'];
8401
            $found   = false;
8402
            for ($i = 0, $iMax = count($this->cookies); $i < $iMax; ++$i) {
8403
                $cookie = $this->cookies[$i];
8404
                if (!is_array($cookie)) {
8405
                    continue;
8406
                }
8407
                if (!isset($cookie['name'])) {
8408
                    continue;
8409
                }
8410
                if ($newName !== $cookie['name']) {
8411
                    continue;
8412
                }
8413
                $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
8414
                $domain    = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
8415
                if ($newDomain !== $domain) {
8416
                    continue;
8417
                }
8418
                $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
8419
                $path    = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
8420
                if ($newPath !== $path) {
8421
                    continue;
8422
                }
8423
                $this->cookies[$i] = $newCookie;
8424
                $found             = true;
8425
                $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
8426
                break;
8427
            }
8428
            if (!$found) {
8429
                $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
8430
                $this->cookies[] = $newCookie;
8431
            }
8432
        }
8433
8434
        return true;
8435
    }
8436
}
8437
8438
if (!extension_loaded('soap')) {
8439
    /**
8440
     *    For backwards compatiblity, define Soapclient unless the PHP SOAP extension is loaded.
8441
     */
8442
    class Soapclient extends Nusoap_client
8443
    {
8444
    }
8445
}
8446