Passed
Branch feature/php-cs-fixer (c730cc)
by Michael
07:22 queued 03:48
created

nusoap_base::contractQName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

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

569
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, false, /** @scrutinizer ignore-type */ false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $name_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

569
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, /** @scrutinizer ignore-type */ false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

569
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, false, false, /** @scrutinizer ignore-type */ false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

569
                        $pXml = isset($pXml) ? $pXml . $this->serialize_val($v, $k, /** @scrutinizer ignore-type */ false, false, false, false, $use) : $this->serialize_val($v, $k, false, false, false, false, $use);
Loading history...
570
                    }
571
                }
572
                if (isset($type) && isset($type_prefix)) {
573
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
574
                } else {
575
                    $type_str = '';
576
                }
577
                if ('literal' == $use) {
578
                    $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...
579
                } else {
580
                    $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
581
                }
582
                break;
583
                break;
584
            case (is_array($val) || $type):
585
                // detect if struct or array
586
                $valueType = $this->isArraySimpleOrStruct($val);
587
                if ('arraySimple' == $valueType || preg_match('/^ArrayOf/', $type)) {
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type false; 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

587
                if ('arraySimple' == $valueType || preg_match('/^ArrayOf/', /** @scrutinizer ignore-type */ $type)) {
Loading history...
588
                    $this->debug('serialize_val: serialize array');
589
                    $i = 0;
590
                    if (is_array($val) && count($val) > 0) {
591
                        foreach ($val as $v) {
592
                            if (is_object($v) && 'soapval' == get_class($v)) {
593
                                $tt_ns = $v->type_ns;
594
                                $tt = $v->type;
595
                            } elseif (is_array($v)) {
596
                                $tt = $this->isArraySimpleOrStruct($v);
597
                            } else {
598
                                $tt = gettype($v);
599
                            }
600
                            $array_types[$tt] = 1;
601
                            // TODO: for literal, the name should be $name
602
                            $xml .= $this->serialize_val($v, 'item', false, false, false, false, $use);
603
                            ++$i;
604
                        }
605
                        if (count($array_types) > 1) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $array_types seems to be defined by a foreach iteration on line 591. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
606
                            $array_typename = 'xsd:anyType';
607
                        } elseif (isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
608
                            if ('integer' == $tt) {
609
                                $tt = 'int';
610
                            }
611
                            $array_typename = 'xsd:' . $tt;
612
                        } elseif (isset($tt) && 'arraySimple' == $tt) {
613
                            $array_typename = 'SOAP-ENC:Array';
614
                        } elseif (isset($tt) && 'arrayStruct' == $tt) {
615
                            $array_typename = 'unnamed_struct_use_soapval';
616
                        } else {
617
                            // if type is prefixed, create type prefix
618
                            if ('' != $tt_ns && $tt_ns == $this->namespaces['xsd']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tt_ns does not seem to be defined for all execution paths leading up to this point.
Loading history...
619
                                $array_typename = 'xsd:' . $tt;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tt seems to be defined by a foreach iteration on line 591. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
620
                            } elseif ($tt_ns) {
621
                                $tt_prefix = 'ns' . mt_rand(1000, 9999);
622
                                $array_typename = "$tt_prefix:$tt";
623
                                $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
624
                            } else {
625
                                $array_typename = $tt;
626
                            }
627
                        }
628
                        $array_type = $i;
629
                        if ('literal' == $use) {
630
                            $type_str = '';
631
                        } elseif (isset($type) && isset($type_prefix)) {
632
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
633
                        } else {
634
                            $type_str = ' xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="' . $array_typename . "[$array_type]\"";
635
                        }
636
                        // empty array
637
                    } else {
638
                        if ('literal' == $use) {
639
                            $type_str = '';
640
                        } elseif (isset($type) && isset($type_prefix)) {
641
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
642
                        } else {
643
                            $type_str = ' xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:anyType[0]"';
644
                        }
645
                    }
646
                    // TODO: for array in literal, there is no wrapper here
647
                    $xml = "<$name$xmlns$type_str$atts>" . $xml . "</$name>";
648
                } else {
649
                    // got a struct
650
                    $this->debug('serialize_val: serialize struct');
651
                    if (isset($type) && isset($type_prefix)) {
652
                        $type_str = " xsi:type=\"$type_prefix:$type\"";
653
                    } else {
654
                        $type_str = '';
655
                    }
656
                    if ('literal' == $use) {
657
                        $xml .= "<$name$xmlns$atts>";
658
                    } else {
659
                        $xml .= "<$name$xmlns$type_str$atts>";
660
                    }
661
                    foreach ($val as $k => $v) {
662
                        // Apache Map
663
                        if ('Map' == $type && 'http://xml.apache.org/xml-soap' == $type_ns) {
664
                            $xml .= '<item>';
665
                            $xml .= $this->serialize_val($k, 'key', false, false, false, false, $use);
666
                            $xml .= $this->serialize_val($v, 'value', false, false, false, false, $use);
667
                            $xml .= '</item>';
668
                        } else {
669
                            $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
670
                        }
671
                    }
672
                    $xml .= "</$name>";
673
                }
674
                break;
675
            default:
676
                $this->debug('serialize_val: serialize unknown');
677
                $xml .= 'not detected, got ' . gettype($val) . ' for ' . $val;
678
                break;
679
        }
680
        $this->debug("serialize_val returning $xml");
681
        return $xml;
682
    }
683
684
    /**
685
     * serializes a message
686
     *
687
     * @param string $body the XML of the SOAP body
688
     * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
689
     * @param array $namespaces optional the namespaces used in generating the body and headers
690
     * @param string $style optional (rpc|document)
691
     * @param string $use optional (encoded|literal)
692
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
693
     * @return string the message
694
     * @access public
695
     */
696
    public function serializeEnvelope($body, $headers = false, $namespaces = [], $style = 'rpc', $use = 'encoded', $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/')
697
    {
698
        // TODO: add an option to automatically run utf8_encode on $body and $headers
699
        // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
700
        // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
701
702
        $this->debug('In serializeEnvelope length=' . strlen($body) . ' body (max 1000 characters)=' . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
703
        $this->debug('headers:');
704
        $this->appendDebug($this->varDump($headers));
705
        $this->debug('namespaces:');
706
        $this->appendDebug($this->varDump($namespaces));
707
708
        // serialize namespaces
709
        $ns_string = '';
710
        foreach (array_merge($this->namespaces, $namespaces) as $k => $v) {
711
            $ns_string .= " xmlns:$k=\"$v\"";
712
        }
713
        if ($encodingStyle) {
714
            $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
715
        }
716
717
        // serialize headers
718
        if ($headers) {
719
            if (is_array($headers)) {
720
                $xml = '';
721
                foreach ($headers as $k => $v) {
722
                    if (is_object($v) && 'soapval' == get_class($v)) {
723
                        $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $name_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

723
                        $xml .= $this->serialize_val($v, false, false, /** @scrutinizer ignore-type */ false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $name of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

723
                        $xml .= $this->serialize_val($v, /** @scrutinizer ignore-type */ false, false, false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type array expected by parameter $attributes of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

723
                        $xml .= $this->serialize_val($v, false, false, false, false, /** @scrutinizer ignore-type */ false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

723
                        $xml .= $this->serialize_val($v, false, false, false, /** @scrutinizer ignore-type */ false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

723
                        $xml .= $this->serialize_val($v, false, /** @scrutinizer ignore-type */ false, false, false, false, $use);
Loading history...
724
                    } else {
725
                        $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
726
                    }
727
                }
728
                $headers = $xml;
729
                $this->debug("In serializeEnvelope, serialized array of headers to $headers");
730
            }
731
            $headers = '<SOAP-ENV:Header>' . $headers . '</SOAP-ENV:Header>';
732
        }
733
        // serialize envelope
734
        return
735
            '<?xml version="1.0" encoding="' . $this->soap_defencoding . '"?' . '>' .
736
            '<SOAP-ENV:Envelope' . $ns_string . '>' .
737
            $headers . '<SOAP-ENV:Body>' .
0 ignored issues
show
Bug introduced by
Are you sure $headers of type false|string|mixed can be used in concatenation? ( Ignorable by Annotation )

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

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

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

1180
        $this->namespaces = array_merge($this->namespaces, /** @scrutinizer ignore-type */ $namespaces);
Loading history...
1181
1182
        // parse schema file
1183
        if ('' != $schema) {
1184
            $this->debug('initial schema file: ' . $schema);
1185
            $this->parseFile($schema, 'schema');
1186
        }
1187
1188
        // parse xml file
1189
        if ('' != $xml) {
1190
            $this->debug('initial xml file: ' . $xml);
1191
            $this->parseFile($xml, 'xml');
1192
        }
1193
    }
1194
1195
    /**
1196
     * parse an XML file
1197
     *
1198
     * @param string $xml path/URL to XML file
1199
     * @param string $type (schema | xml)
1200
     * @return boolean
1201
     * @access public
1202
     */
1203
    public function parseFile($xml, $type)
1204
    {
1205
        // parse xml file
1206
        if ('' != $xml) {
1207
            $xmlStr = @implode('', @file($xml));
0 ignored issues
show
Bug introduced by
It seems like @file($xml) can also be of type false; however, parameter $pieces of implode() 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

1207
            $xmlStr = @implode('', /** @scrutinizer ignore-type */ @file($xml));
Loading history...
1208
            if ('' == $xmlStr) {
1209
                $msg = 'Error reading XML from ' . $xml;
1210
                $this->setError($msg);
1211
                $this->debug($msg);
1212
                return false;
1213
            } else {
1214
                $this->debug("parsing $xml");
1215
                $this->parseString($xmlStr, $type);
1216
                $this->debug("done parsing $xml");
1217
                return true;
1218
            }
1219
        }
1220
        return false;
1221
    }
1222
1223
    /**
1224
     * parse an XML string
1225
     *
1226
     * @param    string $xml path or URL
1227
     * @param    string $type (schema|xml)
1228
     * @access   private
1229
     */
1230
    private function parseString($xml, $type)
1231
    {
1232
        // parse xml string
1233
        if ('' != $xml) {
1234
1235
            // Create an XML parser.
1236
            $this->parser = xml_parser_create();
1237
            // Set the options for parsing the XML data.
1238
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1239
1240
            // Set the object for the parser.
1241
            xml_set_object($this->parser, $this);
1242
1243
            // Set the element handlers for the parser.
1244
            if ('schema' == $type) {
1245
                xml_set_element_handler($this->parser, 'schemaStartElement', 'schemaEndElement');
1246
                xml_set_character_data_handler($this->parser, 'schemaCharacterData');
1247
            } elseif ('xml' == $type) {
1248
                xml_set_element_handler($this->parser, 'xmlStartElement', 'xmlEndElement');
1249
                xml_set_character_data_handler($this->parser, 'xmlCharacterData');
1250
            }
1251
1252
            // Parse the XML file.
1253
            if (!xml_parse($this->parser, $xml, true)) {
1254
                // Display an error message.
1255
                $errstr = sprintf(
1256
                    'XML error parsing XML schema on line %d: %s',
1257
                    xml_get_current_line_number($this->parser),
1258
                    xml_error_string(xml_get_error_code($this->parser))
1259
                );
1260
                $this->debug($errstr);
1261
                $this->debug("XML payload:\n" . $xml);
1262
                $this->setError($errstr);
1263
            }
1264
1265
            xml_parser_free($this->parser);
1266
            unset($this->parser);
1267
        } else {
1268
            $this->debug('no xml passed to parseString()!!');
1269
            $this->setError('no xml passed to parseString()!!');
1270
        }
1271
    }
1272
1273
    /**
1274
     * gets a type name for an unnamed type
1275
     *
1276
     * @param    string    Element name
0 ignored issues
show
Bug introduced by
The type Element was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1277
     * @return    string    A type name for an unnamed type
1278
     * @access    private
1279
     */
1280
    private function CreateTypeName($ename)
1281
    {
1282
        $scope = '';
1283
        for ($i = 0, $iMax = count($this->complexTypeStack); $i < $iMax; $i++) {
1284
            $scope .= $this->complexTypeStack[$i] . '_';
1285
        }
1286
        return $scope . $ename . '_ContainedType';
1287
    }
1288
1289
    /**
1290
     * start-element handler
1291
     *
1292
     * @param    string $parser XML parser object
1293
     * @param    string $name element name
1294
     * @param    string|array $attrs associative array of attributes
1295
     * @access   private
1296
     */
1297
    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

1297
    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...
1298
    {
1299
1300
        // position in the total number of elements, starting from 0
1301
        $pos = $this->position++;
1302
        $depth = $this->depth++;
1303
        // set self as current value for this depth
1304
        $this->depth_array[$depth] = $pos;
1305
        $this->message[$pos] = ['cdata' => ''];
1306
        if ($depth > 0) {
1307
            $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1308
        } else {
1309
            $this->defaultNamespace[$pos] = false;
1310
        }
1311
1312
        // get element prefix
1313
        if ($prefix = $this->getPrefix($name)) {
1314
            // get unqualified name
1315
            $name = $this->getLocalPart($name);
1316
        } else {
1317
            $prefix = '';
1318
        }
1319
1320
        // loop thru attributes, expanding, and registering namespace declarations
1321
        if (count($attrs) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $attrs can also be of type string; however, parameter $var of count() does only seem to accept Countable|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

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

1625
    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...
1626
    {
1627
        // bring depth down a notch
1628
        $this->depth--;
1629
        // position of current element is equal to the last value left in depth_array for my depth
1630
        if (isset($this->depth_array[$this->depth])) {
1631
            $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...
1632
        }
1633
        // get element prefix
1634
        if ($prefix = $this->getPrefix($name)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $prefix is dead and can be removed.
Loading history...
1635
            // get unqualified name
1636
            $name = $this->getLocalPart($name);
1637
        } else {
1638
            $prefix = '';
1639
        }
1640
        // move on...
1641
        if ('complexType' == $name) {
1642
            $this->xdebug('done processing complexType ' . ($this->currentComplexType ?: '(unknown)'));
1643
            $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1644
            $this->currentComplexType = array_pop($this->complexTypeStack);
1645
            //$this->currentElement = false;
1646
        }
1647
        if ('element' == $name) {
1648
            $this->xdebug('done processing element ' . ($this->currentElement ?: '(unknown)'));
1649
            $this->currentElement = array_pop($this->elementStack);
1650
        }
1651
        if ('simpleType' == $name) {
1652
            $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ?: '(unknown)'));
1653
            $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1654
            $this->currentSimpleType = array_pop($this->simpleTypeStack);
1655
        }
1656
    }
1657
1658
    /**
1659
     * element content handler
1660
     *
1661
     * @param    string $parser XML parser object
1662
     * @param    string $data element content
1663
     * @access   private
1664
     */
1665
    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

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

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

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

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

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

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

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

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

2709
                $A2 = $this->request_method . ':' . /** @scrutinizer ignore-type */ $this->digest_uri;
Loading history...
2710
2711
                // H(A2)
2712
                $HA2 = md5($A2);
2713
2714
                // KD(secret, data) = H(concat(secret, ":", data))
2715
                // if qop == auth:
2716
                // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2717
                //                              ":" nc-value
2718
                //                              ":" unq(cnonce-value)
2719
                //                              ":" unq(qop-value)
2720
                //                              ":" H(A2)
2721
                //                            ) <">
2722
                // if qop is missing,
2723
                // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2724
2725
                $unhashedDigest = '';
2726
                $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2727
                $cnonce = $nonce;
2728
                if ('' != $digestRequest['qop']) {
2729
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf('%08d', $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2730
                } else {
2731
                    $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2732
                }
2733
2734
                $hashedDigest = md5($unhashedDigest);
2735
2736
                $opaque = '';
2737
                if (isset($digestRequest['opaque'])) {
2738
                    $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2739
                }
2740
2741
                $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf('%08x', $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
2742
            }
2743
        } elseif ('certificate' == $authtype) {
2744
            $this->certRequest = $certRequest;
2745
            $this->debug('Authorization header not set for certificate');
2746
        } elseif ('ntlm' == $authtype) {
2747
            // do nothing
2748
            $this->debug('Authorization header not set for ntlm');
2749
        }
2750
        $this->username = $username;
2751
        $this->password = $password;
2752
        $this->authtype = $authtype;
2753
        $this->digestRequest = $digestRequest;
2754
    }
2755
2756
    /**
2757
     * set the soapaction value
2758
     *
2759
     * @param    string $soapaction
2760
     * @access   public
2761
     */
2762
    public function setSOAPAction($soapaction)
2763
    {
2764
        $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2765
    }
2766
2767
    /**
2768
     * use http encoding
2769
     *
2770
     * @param    string $enc encoding style. supported values: gzip, deflate, or both
2771
     * @access   public
2772
     */
2773
    public function setEncoding($enc = 'gzip, deflate')
2774
    {
2775
        if (function_exists('gzdeflate')) {
2776
            $this->protocol_version = '1.1';
2777
            $this->setHeader('Accept-Encoding', $enc);
2778
            if (!isset($this->outgoing_headers['Connection'])) {
2779
                $this->setHeader('Connection', 'close');
2780
                $this->persistentConnection = false;
2781
            }
2782
            // deprecated as of PHP 5.3.0
2783
            //set_magic_quotes_runtime(0);
2784
            $this->encoding = $enc;
2785
        }
2786
    }
2787
2788
    /**
2789
     * set proxy info here
2790
     *
2791
     * @param    string $proxyhost use an empty string to remove proxy
2792
     * @param    string $proxyport
2793
     * @param    string $proxyusername
2794
     * @param    string $proxypassword
2795
     * @param    string $proxyauthtype (basic|ntlm)
2796
     * @access   public
2797
     */
2798
    public function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic')
2799
    {
2800
        if ($proxyhost) {
2801
            $this->proxy = [
2802
                'host' => $proxyhost,
2803
                'port' => $proxyport,
2804
                'username' => $proxyusername,
2805
                'password' => $proxypassword,
2806
                'authtype' => $proxyauthtype
2807
            ];
2808
            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...
2809
                $this->setHeader('Proxy-Authorization', ' Basic ' . base64_encode($proxyusername . ':' . $proxypassword));
2810
            }
2811
        } else {
2812
            $this->debug('remove proxy');
2813
            $proxy = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $proxy is dead and can be removed.
Loading history...
2814
            unsetHeader('Proxy-Authorization');
0 ignored issues
show
Bug introduced by
The function unsetHeader was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2814
            /** @scrutinizer ignore-call */ 
2815
            unsetHeader('Proxy-Authorization');
Loading history...
2815
        }
2816
    }
2817
2818
2819
    /**
2820
     * Test if the given string starts with a header that is to be skipped.
2821
     * Skippable headers result from chunked transfer and proxy requests.
2822
     *
2823
     * @param    string $data The string to check.
2824
     * @returns    boolean    Whether a skippable header was found.
2825
     * @access    private
2826
     */
2827
    private function isSkippableCurlHeader(&$data)
2828
    {
2829
        $skipHeaders = [
2830
            'HTTP/1.1 100',
2831
            'HTTP/1.0 301',
2832
            'HTTP/1.1 301',
2833
            'HTTP/1.0 302',
2834
            'HTTP/1.1 302',
2835
            'HTTP/1.0 401',
2836
            'HTTP/1.1 401',
2837
            'HTTP/1.0 200 Connection established'
2838
        ];
2839
        foreach ($skipHeaders as $hd) {
2840
            $prefix = substr($data, 0, strlen($hd));
2841
            if ($prefix == $hd) {
2842
                return true;
2843
            }
2844
        }
2845
2846
        return false;
2847
    }
2848
2849
    /**
2850
     * decode a string that is encoded w/ "chunked' transfer encoding
2851
     * as defined in RFC2068 19.4.6
2852
     *
2853
     * @param    string $buffer
2854
     * @param    string $lb
2855
     * @returns    string
2856
     * @access   public
2857
     * @deprecated
2858
     */
2859
    public function decodeChunked($buffer, $lb)
2860
    {
2861
        // length := 0
2862
        $length = 0;
2863
        $new = '';
2864
2865
        // read chunk-size, chunk-extension (if any) and CRLF
2866
        // get the position of the linebreak
2867
        $chunkend = strpos($buffer, $lb);
2868
        if (false == $chunkend) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $chunkend of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
2869
            $this->debug('no linebreak found in decodeChunked');
2870
            return $new;
2871
        }
2872
        $temp = substr($buffer, 0, $chunkend);
2873
        $chunk_size = hexdec(trim($temp));
2874
        $chunkstart = $chunkend + strlen($lb);
2875
        // while (chunk-size > 0) {
2876
        while ($chunk_size > 0) {
2877
            $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2878
            $chunkend = 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 strpos(). ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

3847
                    fpassthru(/** @scrutinizer ignore-type */ $fp);
Loading history...
3848
                }
3849
            } elseif ($this->wsdl) {
3850
                $this->debug('In service, serialize WSDL');
3851
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3852
                print $this->wsdl->serialize($this->debug_flag);
3853
                if ($this->debug_flag) {
3854
                    $this->debug('wsdl:');
3855
                    $this->appendDebug($this->varDump($this->wsdl));
3856
                    print $this->getDebugAsXMLComment();
3857
                }
3858
            } else {
3859
                $this->debug('In service, there is no WSDL');
3860
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3861
                print 'This service does not provide WSDL';
3862
            }
3863
        } elseif ($this->wsdl) {
3864
            $this->debug('In service, return Web description');
3865
            print $this->wsdl->webDescription();
3866
        } else {
3867
            $this->debug('In service, no Web description');
3868
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3869
            print 'This service does not provide a Web description';
3870
        }
3871
    }
3872
3873
    /**
3874
     * parses HTTP request headers.
3875
     *
3876
     * The following fields are set by this function (when successful)
3877
     *
3878
     * headers
3879
     * request
3880
     * xml_encoding
3881
     * SOAPAction
3882
     *
3883
     * @access   private
3884
     */
3885
    private function parse_http_headers()
3886
    {
3887
        global $HTTP_SERVER_VARS;
3888
3889
        $this->request = '';
3890
        $this->SOAPAction = '';
3891
        if (function_exists('getallheaders')) {
3892
            $this->debug('In parse_http_headers, use getallheaders');
3893
            $headers = getallheaders();
3894
            foreach ($headers as $k => $v) {
3895
                $k = strtolower($k);
3896
                $this->headers[$k] = $v;
3897
                $this->request .= "$k: $v\r\n";
3898
                $this->debug("$k: $v");
3899
            }
3900
            // get SOAPAction header
3901
            if (isset($this->headers['soapaction'])) {
3902
                $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
3903
            }
3904
            // get the character encoding of the incoming request
3905
            if (isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) {
3906
                $enc = str_replace('"', '', substr(strstr($this->headers['content-type'], '='), 1));
3907
                if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3908
                    $this->xml_encoding = strtoupper($enc);
3909
                } else {
3910
                    $this->xml_encoding = 'US-ASCII';
3911
                }
3912
            } else {
3913
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3914
                $this->xml_encoding = 'ISO-8859-1';
3915
            }
3916
        } elseif (isset($_SERVER) && is_array($_SERVER)) {
3917
            $this->debug('In parse_http_headers, use _SERVER');
3918
            foreach ($_SERVER as $k => $v) {
3919
                if ('HTTP_' == substr($k, 0, 5)) {
3920
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3921
                } else {
3922
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3923
                }
3924
                if ('soapaction' == $k) {
3925
                    // get SOAPAction header
3926
                    $k = 'SOAPAction';
3927
                    $v = str_replace('"', '', $v);
3928
                    $v = str_replace('\\', '', $v);
3929
                    $this->SOAPAction = $v;
3930
                } elseif ('content-type' == $k) {
3931
                    // get the character encoding of the incoming request
3932
                    if (strpos($v, '=')) {
3933
                        $enc = substr(strstr($v, '='), 1);
3934
                        $enc = str_replace('"', '', $enc);
3935
                        $enc = str_replace('\\', '', $enc);
3936
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3937
                            $this->xml_encoding = strtoupper($enc);
3938
                        } else {
3939
                            $this->xml_encoding = 'US-ASCII';
3940
                        }
3941
                    } else {
3942
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3943
                        $this->xml_encoding = 'ISO-8859-1';
3944
                    }
3945
                }
3946
                $this->headers[$k] = $v;
3947
                $this->request .= "$k: $v\r\n";
3948
                $this->debug("$k: $v");
3949
            }
3950
        } elseif (is_array($HTTP_SERVER_VARS)) {
3951
            $this->debug('In parse_http_headers, use HTTP_SERVER_VARS');
3952
            foreach ($HTTP_SERVER_VARS as $k => $v) {
3953
                if ('HTTP_' == substr($k, 0, 5)) {
3954
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3955
                    $k = strtolower(substr($k, 5));
3956
                } else {
3957
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3958
                    $k = strtolower($k);
3959
                }
3960
                if ('soapaction' == $k) {
3961
                    // get SOAPAction header
3962
                    $k = 'SOAPAction';
3963
                    $v = str_replace('"', '', $v);
3964
                    $v = str_replace('\\', '', $v);
3965
                    $this->SOAPAction = $v;
3966
                } elseif ('content-type' == $k) {
3967
                    // get the character encoding of the incoming request
3968
                    if (strpos($v, '=')) {
3969
                        $enc = substr(strstr($v, '='), 1);
3970
                        $enc = str_replace('"', '', $enc);
3971
                        $enc = str_replace('\\', '', $enc);
3972
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
3973
                            $this->xml_encoding = strtoupper($enc);
3974
                        } else {
3975
                            $this->xml_encoding = 'US-ASCII';
3976
                        }
3977
                    } else {
3978
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3979
                        $this->xml_encoding = 'ISO-8859-1';
3980
                    }
3981
                }
3982
                $this->headers[$k] = $v;
3983
                $this->request .= "$k: $v\r\n";
3984
                $this->debug("$k: $v");
3985
            }
3986
        } else {
3987
            $this->debug('In parse_http_headers, HTTP headers not accessible');
3988
            $this->setError('HTTP headers not accessible');
3989
        }
3990
    }
3991
3992
    /**
3993
     * parses a request
3994
     *
3995
     * The following fields are set by this function (when successful)
3996
     *
3997
     * headers
3998
     * request
3999
     * xml_encoding
4000
     * SOAPAction
4001
     * request
4002
     * requestSOAP
4003
     * methodURI
4004
     * methodname
4005
     * methodparams
4006
     * requestHeaders
4007
     * document
4008
     *
4009
     * This sets the fault field on error
4010
     *
4011
     * @param    string $data XML string
4012
     * @access   private
4013
     */
4014
    private function parse_request($data = '')
4015
    {
4016
        $this->debug('entering parse_request()');
4017
        $this->parse_http_headers();
4018
        $this->debug('got character encoding: ' . $this->xml_encoding);
4019
        // uncompress if necessary
4020
        if (isset($this->headers['content-encoding']) && '' != $this->headers['content-encoding']) {
4021
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
4022
            if ('deflate' == $this->headers['content-encoding'] || 'gzip' == $this->headers['content-encoding']) {
4023
                // if decoding works, use it. else assume data wasn't gzencoded
4024
                if (function_exists('gzuncompress')) {
4025
                    if ('deflate' == $this->headers['content-encoding'] && $degzdata = @gzuncompress($data)) {
4026
                        $data = $degzdata;
4027
                    } elseif ('gzip' == $this->headers['content-encoding'] && $degzdata = gzinflate(substr($data, 10))) {
4028
                        $data = $degzdata;
4029
                    } else {
4030
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
4031
                        return;
4032
                    }
4033
                } else {
4034
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
4035
                    return;
4036
                }
4037
            }
4038
        }
4039
        $this->request .= "\r\n" . $data;
4040
        $data = $this->parseRequest($this->headers, $data);
4041
        $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...
4042
        $this->debug('leaving parse_request');
4043
    }
4044
4045
    /**
4046
     * invokes a PHP function for the requested SOAP method
4047
     *
4048
     * The following fields are set by this function (when successful)
4049
     *
4050
     * methodreturn
4051
     *
4052
     * Note that the PHP function that is called may also set the following
4053
     * fields to affect the response sent to the client
4054
     *
4055
     * responseHeaders
4056
     * outgoing_headers
4057
     *
4058
     * This sets the fault field on error
4059
     *
4060
     * @access   private
4061
     */
4062
    private function invoke_method()
4063
    {
4064
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
4065
4066
        //
4067
        // if you are debugging in this area of the code, your service uses a class to implement methods,
4068
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
4069
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
4070
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
4071
        // the XML request and reading the XML response.  you need to add the RequestElementName and
4072
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
4073
        // generates for the method.  these parameters are used to specify the correct XML element names
4074
        // for .NET to use, i.e. the names with the '.' in them.
4075
        //
4076
        $orig_methodname = $this->methodname;
4077
        if ($this->wsdl) {
4078
            if ($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...
4079
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
4080
                $this->appendDebug('opData=' . $this->varDump($this->opData));
4081
            } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
4082
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
4083
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
4084
                $this->appendDebug('opData=' . $this->varDump($this->opData));
4085
                $this->methodname = $this->opData['name'];
4086
            } else {
4087
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
4088
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
4089
                return;
4090
            }
4091
        } else {
4092
            $this->debug('in invoke_method, no WSDL to validate method');
4093
        }
4094
4095
        // if a . is present in $this->methodname, we see if there is a class in scope,
4096
        // which could be referred to. We will also distinguish between two deliminators,
4097
        // to allow methods to be called a the class or an instance
4098
        if (strpos($this->methodname, '..') > 0) {
4099
            $delim = '..';
4100
        } elseif (strpos($this->methodname, '.') > 0) {
4101
            $delim = '.';
4102
        } else {
4103
            $delim = '';
4104
        }
4105
        $this->debug("in invoke_method, delim=$delim");
4106
4107
        $class = '';
4108
        $method = '';
4109
        if (strlen($delim) > 0 && 1 == substr_count($this->methodname, $delim)) {
4110
            $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
4111
            if (class_exists($try_class)) {
4112
                // get the class and method name
4113
                $class = $try_class;
4114
                $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
4115
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4116
            } else {
4117
                $this->debug("in invoke_method, class=$try_class not found");
4118
            }
4119
        } elseif (strlen($delim) > 0 && substr_count($this->methodname, $delim) > 1) {
4120
            $split = explode($delim, $this->methodname);
4121
            $method = array_pop($split);
4122
            $class = implode('\\', $split);
4123
        } else {
4124
            $try_class = '';
4125
            $this->debug('in invoke_method, no class to try');
4126
        }
4127
4128
        // does method exist?
4129
        if ('' == $class) {
4130
            if (!function_exists($this->methodname)) {
4131
                $this->debug("in invoke_method, function '$this->methodname' not found!");
4132
                $this->result = 'fault: method not found';
4133
                $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...
4134
                return;
4135
            }
4136
        } else {
4137
            $method_to_compare = ('4.' == substr(phpversion(), 0, 2)) ? strtolower($method) : $method;
4138
            if (!in_array($method_to_compare, get_class_methods($class))) {
4139
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4140
                $this->result = 'fault: method not found';
4141
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4142
                return;
4143
            }
4144
        }
4145
4146
        // evaluate message, getting back parameters
4147
        // verify that request parameters match the method's signature
4148
        if (!$this->verify_method($this->methodname, $this->methodparams)) {
4149
            // debug
4150
            $this->debug('ERROR: request not verified against method signature');
4151
            $this->result = 'fault: request failed validation against method signature';
4152
            // return fault
4153
            $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
4154
            return;
4155
        }
4156
4157
        // if there are parameters to pass
4158
        $this->debug('in invoke_method, params:');
4159
        $this->appendDebug($this->varDump($this->methodparams));
4160
        $this->debug("in invoke_method, calling '$this->methodname'");
4161
        if (!function_exists('call_user_func_array')) {
4162
            if ('' == $class) {
4163
                $this->debug('in invoke_method, calling function using eval()');
4164
                $funcCall = "\$this->methodreturn = $this->methodname(";
4165
            } else {
4166
                if ('..' == $delim) {
4167
                    $this->debug('in invoke_method, calling class method using eval()');
4168
                    $funcCall = '$this->methodreturn = ' . $class . '::' . $method . '(';
4169
                } else {
4170
                    $this->debug('in invoke_method, calling instance method using eval()');
4171
                    // generate unique instance name
4172
                    $instname = '$inst_' . time();
4173
                    $funcCall = $instname . ' = new ' . $class . '(); ';
4174
                    $funcCall .= '$this->methodreturn = ' . $instname . '->' . $method . '(';
4175
                }
4176
            }
4177
            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...
4178
                foreach ($this->methodparams as $param) {
4179
                    if (is_array($param) || is_object($param)) {
4180
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4181
                        return;
4182
                    }
4183
                    $funcCall .= "\"$param\",";
4184
                }
4185
                $funcCall = substr($funcCall, 0, -1);
4186
            }
4187
            $funcCall .= ');';
4188
            $this->debug('in invoke_method, function call: ' . $funcCall);
4189
            @eval($funcCall);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
4190
        } else {
4191
            if ('' == $class) {
4192
                $this->debug('in invoke_method, calling function using call_user_func_array()');
4193
                $call_arg = $this->methodname;    // straight assignment changes $this->methodname to lower case after call_user_func_array()
4194
            } elseif ('..' == $delim) {
4195
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
4196
                $call_arg = [$class, $method];
4197
            } else {
4198
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4199
                $instance = new $class();
4200
                $call_arg = [&$instance, $method];
4201
            }
4202
            if (is_array($this->methodparams)) {
0 ignored issues
show
introduced by
The condition is_array($this->methodparams) is always true.
Loading history...
4203
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4204
            } else {
4205
                $this->methodreturn = call_user_func_array($call_arg, []);
4206
            }
4207
        }
4208
        $this->debug('in invoke_method, methodreturn:');
4209
        $this->appendDebug($this->varDump($this->methodreturn));
4210
        $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
4211
    }
4212
4213
    /**
4214
     * serializes the return value from a PHP function into a full SOAP Envelope
4215
     *
4216
     * The following fields are set by this function (when successful)
4217
     *
4218
     * responseSOAP
4219
     *
4220
     * This sets the fault field on error
4221
     *
4222
     * @access   private
4223
     */
4224
    private function serialize_return()
4225
    {
4226
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4227
        // if fault
4228
        if (isset($this->methodreturn) && is_object($this->methodreturn) && (('soap_fault' == get_class($this->methodreturn)) || ('nusoap_fault' == get_class($this->methodreturn)))) {
4229
            $this->debug('got a fault object from method');
4230
            $this->fault = $this->methodreturn;
4231
            return;
4232
        } elseif ($this->methodreturnisliteralxml) {
4233
            $return_val = $this->methodreturn;
4234
        // returned value(s)
4235
        } else {
4236
            $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
4237
            $this->debug('serializing return value');
4238
            if ($this->wsdl) {
4239
                if (count($this->opData['output']['parts']) > 1) {
4240
                    $this->debug('more than one output part, so use the method return unchanged');
4241
                    $opParams = $this->methodreturn;
4242
                } elseif (1 == count($this->opData['output']['parts'])) {
4243
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
4244
                    // TODO: verify that it is not already wrapped!
4245
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
4246
                    //	$this->debug('wrap in element named ' . $name);
4247
                    //}
4248
                    $opParams = [$this->methodreturn];
4249
                }
4250
                $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...
4251
                $this->appendDebug($this->wsdl->getDebug());
4252
                $this->wsdl->clearDebug();
4253
                if ($errstr = $this->wsdl->getError()) {
4254
                    $this->debug('got wsdl error: ' . $errstr);
4255
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4256
                    return;
4257
                }
4258
            } else {
4259
                if (isset($this->methodreturn)) {
4260
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
4261
                } else {
4262
                    $return_val = '';
4263
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
4264
                }
4265
            }
4266
        }
4267
        $this->debug('return value:');
4268
        $this->appendDebug($this->varDump($return_val));
4269
4270
        $this->debug('serializing response');
4271
        if ($this->wsdl) {
4272
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4273
            if ('rpc' == $this->opData['style']) {
4274
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4275
                if ('literal' == $this->opData['output']['use']) {
4276
                    // 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
4277
                    if ($this->methodURI) {
4278
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4279
                    } else {
4280
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4281
                    }
4282
                } else {
4283
                    if ($this->methodURI) {
4284
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4285
                    } else {
4286
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
4287
                    }
4288
                }
4289
            } else {
4290
                $this->debug('style is not rpc for serialization: assume document');
4291
                $payload = $return_val;
4292
            }
4293
        } else {
4294
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4295
            $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . 'Response>';
4296
        }
4297
        $this->result = 'successful';
4298
        if ($this->wsdl) {
4299
            //if($this->debug_flag){
4300
            $this->appendDebug($this->wsdl->getDebug());
4301
            //	}
4302
            if (isset($this->opData['output']['encodingStyle'])) {
4303
                $encodingStyle = $this->opData['output']['encodingStyle'];
4304
            } else {
4305
                $encodingStyle = '';
4306
            }
4307
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4308
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
4309
        } else {
4310
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
4311
        }
4312
        $this->debug('Leaving serialize_return');
4313
    }
4314
4315
    /**
4316
     * sends an HTTP response
4317
     *
4318
     * The following fields are set by this function (when successful)
4319
     *
4320
     * outgoing_headers
4321
     * response
4322
     *
4323
     * @access   private
4324
     */
4325
    private function send_response()
4326
    {
4327
        $this->debug('Enter send_response');
4328
        if ($this->fault) {
4329
            $payload = $this->fault->serialize();
4330
            $this->outgoing_headers[] = 'HTTP/1.0 500 Internal Server Error';
4331
            $this->outgoing_headers[] = 'Status: 500 Internal Server Error';
4332
        } else {
4333
            $payload = $this->responseSOAP;
4334
            // Some combinations of PHP+Web server allow the Status
4335
            // to come through as a header.  Since OK is the default
4336
            // just do nothing.
4337
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4338
            // $this->outgoing_headers[] = "Status: 200 OK";
4339
        }
4340
        // add debug data if in debug mode
4341
        if (isset($this->debug_flag) && $this->debug_flag) {
4342
            $payload .= $this->getDebugAsXMLComment();
4343
        }
4344
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4345
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4346
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . $rev[1] . ')';
4347
        // Let the Web server decide about this
4348
        //$this->outgoing_headers[] = "Connection: Close\r\n";
4349
        $payload = $this->getHTTPBody($payload);
4350
        $type = $this->getHTTPContentType();
4351
        $charset = $this->getHTTPContentTypeCharset();
4352
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4353
        //begin code to compress payload - by John
4354
        // NOTE: there is no way to know whether the Web server will also compress
4355
        // this data.
4356
        if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4357
            if (false !== strpos($this->headers['accept-encoding'], 'gzip')) {
4358
                if (function_exists('gzencode')) {
4359
                    if (isset($this->debug_flag) && $this->debug_flag) {
4360
                        $payload .= '<!-- Content being gzipped -->';
4361
                    }
4362
                    $this->outgoing_headers[] = 'Content-Encoding: gzip';
4363
                    $payload = gzencode($payload);
4364
                } else {
4365
                    if (isset($this->debug_flag) && $this->debug_flag) {
4366
                        $payload .= '<!-- Content will not be gzipped: no gzencode -->';
4367
                    }
4368
                }
4369
            } elseif (false !== strpos($this->headers['accept-encoding'], 'deflate')) {
4370
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4371
                // instead of gzcompress output,
4372
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4373
                if (function_exists('gzdeflate')) {
4374
                    if (isset($this->debug_flag) && $this->debug_flag) {
4375
                        $payload .= '<!-- Content being deflated -->';
4376
                    }
4377
                    $this->outgoing_headers[] = 'Content-Encoding: deflate';
4378
                    $payload = gzdeflate($payload);
4379
                } else {
4380
                    if (isset($this->debug_flag) && $this->debug_flag) {
4381
                        $payload .= '<!-- Content will not be deflated: no gzcompress -->';
4382
                    }
4383
                }
4384
            }
4385
        }
4386
        //end code
4387
        $this->outgoing_headers[] = 'Content-Length: ' . strlen($payload);
4388
        reset($this->outgoing_headers);
4389
        foreach ($this->outgoing_headers as $hdr) {
4390
            header($hdr, false);
4391
        }
4392
        print $payload;
4393
        $this->response = implode("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
4394
    }
4395
4396
    /**
4397
     * takes the value that was created by parsing the request
4398
     * and compares to the method's signature, if available.
4399
     *
4400
     * @param    string $operation The operation to be invoked
4401
     * @param    array $request The array of parameter values
4402
     * @return    boolean    Whether the operation was found
4403
     * @access   private
4404
     */
4405
    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

4405
    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...
4406
    {
4407
        if (isset($this->wsdl) && is_object($this->wsdl)) {
4408
            if ($this->wsdl->getOperationData($operation)) {
4409
                return true;
4410
            }
4411
        } elseif (isset($this->operations[$operation])) {
4412
            return true;
4413
        }
4414
        return false;
4415
    }
4416
4417
    /**
4418
     * processes SOAP message received from client
4419
     *
4420
     * @param    array $headers The HTTP headers
4421
     * @param    string $data unprocessed request data from client
4422
     * @return    mixed    value of the message, decoded into a PHP type
4423
     * @access   private
4424
     */
4425
    private function parseRequest($headers, $data)
4426
    {
4427
        $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
4428
        $this->appendDebug($this->varDump($headers));
4429
        if (!isset($headers['content-type'])) {
4430
            $this->setError('Request not of type text/xml (no content-type header)');
4431
            return false;
4432
        }
4433
        if (false === strpos($headers['content-type'], 'text/xml')) {
4434
            $this->setError('Request not of type text/xml');
4435
            return false;
4436
        }
4437
        if (strpos($headers['content-type'], '=')) {
4438
            $enc = str_replace('"', '', substr(strstr($headers['content-type'], '='), 1));
4439
            $this->debug('Got response encoding: ' . $enc);
4440
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
4441
                $this->xml_encoding = strtoupper($enc);
4442
            } else {
4443
                $this->xml_encoding = 'US-ASCII';
4444
            }
4445
        } else {
4446
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4447
            $this->xml_encoding = 'ISO-8859-1';
4448
        }
4449
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4450
        // parse response, get soap parser obj
4451
        $parser = new nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
4452
        // parser debug
4453
        $this->debug("parser debug: \n" . $parser->getDebug());
4454
        // if fault occurred during message parsing
4455
        if ($err = $parser->getError()) {
4456
            $this->result = 'fault: error in msg parsing: ' . $err;
4457
            $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
4458
        // else successfully parsed request into soapval object
4459
        } else {
4460
            // get/set methodname
4461
            $this->methodURI = $parser->root_struct_namespace;
4462
            $this->methodname = $parser->root_struct_name;
4463
            $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4464
            $this->debug('calling parser->get_soapbody()');
4465
            $this->methodparams = $parser->get_soapbody();
4466
            // get SOAP headers
4467
            $this->requestHeaders = $parser->getHeaders();
4468
            // get SOAP Header
4469
            $this->requestHeader = $parser->get_soapheader();
4470
            // add document for doclit support
4471
            $this->document = $parser->document;
4472
        }
4473
    }
4474
4475
    /**
4476
     * gets the HTTP body for the current response.
4477
     *
4478
     * @param string $soapmsg The SOAP payload
4479
     * @return string The HTTP body, which includes the SOAP payload
4480
     * @access private
4481
     */
4482
    private function getHTTPBody($soapmsg)
4483
    {
4484
        return $soapmsg;
4485
    }
4486
4487
    /**
4488
     * gets the HTTP content type for the current response.
4489
     *
4490
     * Note: getHTTPBody must be called before this.
4491
     *
4492
     * @return string the HTTP content type for the current response.
4493
     * @access private
4494
     */
4495
    private function getHTTPContentType()
4496
    {
4497
        return 'text/xml';
4498
    }
4499
4500
    /**
4501
     * gets the HTTP content type charset for the current response.
4502
     * returns false for non-text content types.
4503
     *
4504
     * Note: getHTTPBody must be called before this.
4505
     *
4506
     * @return string the HTTP content type charset for the current response.
4507
     * @access private
4508
     */
4509
    private function getHTTPContentTypeCharset()
4510
    {
4511
        return $this->soap_defencoding;
4512
    }
4513
4514
    /**
4515
     * add a method to the dispatch map (this has been replaced by the register method)
4516
     *
4517
     * @param    string $methodname
4518
     * @param    string $in array of input values
4519
     * @param    string $out array of output values
4520
     * @access   public
4521
     * @deprecated
4522
     */
4523
    public function add_to_map($methodname, $in, $out)
4524
    {
4525
        $this->operations[$methodname] = ['name' => $methodname, 'in' => $in, 'out' => $out];
4526
    }
4527
4528
    /**
4529
     * register a service function with the server
4530
     *
4531
     * @param    string $name the name of the PHP function, class.method or class..method
4532
     * @param    array $in assoc array of input values: key = param name, value = param type
4533
     * @param    array $out assoc array of output values: key = param name, value = param type
4534
     * @param    mixed $namespace the element namespace for the method or false
4535
     * @param    mixed $soapaction the soapaction for the method or false
4536
     * @param    mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4537
     * @param    mixed $use optional (encoded|literal) or false
4538
     * @param    string $documentation optional Description to include in WSDL
4539
     * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4540
     * @access   public
4541
     */
4542
    public function register($name, $in = [], $out = [], $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '')
4543
    {
4544
        global $HTTP_SERVER_VARS;
4545
4546
        if ($this->externalWSDLURL) {
4547
            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...
4548
        }
4549
        if (!$name) {
4550
            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...
4551
        }
4552
        if (!is_array($in)) {
0 ignored issues
show
introduced by
The condition is_array($in) is always true.
Loading history...
4553
            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...
4554
        }
4555
        if (!is_array($out)) {
0 ignored issues
show
introduced by
The condition is_array($out) is always true.
Loading history...
4556
            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...
4557
        }
4558
        if (false == $namespace) {
4559
        }
4560
        if (false == $soapaction) {
4561
            if (isset($_SERVER)) {
4562
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
4563
                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4564
                $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4565
            } elseif (isset($HTTP_SERVER_VARS)) {
4566
                $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4567
                $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4568
                $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4569
            } else {
4570
                $this->setError('Neither _SERVER nor HTTP_SERVER_VARS is available');
4571
            }
4572
            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...
4573
                $SCHEME = 'https';
4574
            } else {
4575
                $SCHEME = 'http';
4576
            }
4577
            $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SERVER_NAME does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $SCRIPT_NAME does not seem to be defined for all execution paths leading up to this point.
Loading history...
4578
        }
4579
        if (false == $style) {
4580
            $style = 'rpc';
4581
        }
4582
        if (false == $use) {
4583
            $use = 'encoded';
4584
        }
4585
        if ('encoded' == $use && '' == $encodingStyle) {
4586
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4587
        }
4588
4589
        $this->operations[$name] = [
4590
            'name' => $name,
4591
            'in' => $in,
4592
            'out' => $out,
4593
            'namespace' => $namespace,
4594
            'soapaction' => $soapaction,
4595
            'style' => $style
4596
        ];
4597
        if ($this->wsdl) {
4598
            $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
4599
        }
4600
        return true;
4601
    }
4602
4603
    /**
4604
     * Specify a fault to be returned to the client.
4605
     * This also acts as a flag to the server that a fault has occured.
4606
     *
4607
     * @param    string $faultcode
4608
     * @param    string $faultstring
4609
     * @param    string $faultactor
4610
     * @param    string $faultdetail
4611
     * @access   public
4612
     */
4613
    public function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
4614
    {
4615
        if ('' == $faultdetail && $this->debug_flag) {
4616
            $faultdetail = $this->getDebug();
4617
        }
4618
        $this->fault = new nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
4619
        $this->fault->soap_defencoding = $this->soap_defencoding;
4620
    }
4621
4622
    /**
4623
     * Sets up wsdl object.
4624
     * Acts as a flag to enable internal WSDL generation
4625
     *
4626
     * @param string $serviceName , name of the service
4627
     * @param mixed $namespace optional 'tns' service namespace or false
4628
     * @param mixed $endpoint optional URL of service endpoint or false
4629
     * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4630
     * @param string $transport optional SOAP transport
4631
     * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4632
     */
4633
    public function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4634
    {
4635
        global $HTTP_SERVER_VARS;
4636
4637
        if (isset($_SERVER)) {
4638
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
4639
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
4640
            $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4641
            $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4642
        } elseif (isset($HTTP_SERVER_VARS)) {
4643
            $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4644
            $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4645
            $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4646
            $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4647
        } else {
4648
            $this->setError('Neither _SERVER nor HTTP_SERVER_VARS is available');
4649
        }
4650
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4651
        $colon = 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...
4652
        if ($colon) {
4653
            $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
4654
        }
4655
        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...
4656
            $SERVER_PORT = '';
4657
        } else {
4658
            $SERVER_PORT = ':' . $SERVER_PORT;
4659
        }
4660
        if (false == $namespace) {
4661
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
4662
        }
4663
4664
        if (false == $endpoint) {
4665
            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...
4666
                $SCHEME = 'https';
4667
            } else {
4668
                $SCHEME = 'http';
4669
            }
4670
            $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...
4671
        }
4672
4673
        if (false == $schemaTargetNamespace) {
4674
            $schemaTargetNamespace = $namespace;
4675
        }
4676
4677
        $this->wsdl = new wsdl;
4678
        $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...
4679
        $this->wsdl->endpoint = $endpoint;
4680
        $this->wsdl->namespaces['tns'] = $namespace;
4681
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4682
        $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4683
        if ($schemaTargetNamespace != $namespace) {
4684
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4685
        }
4686
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
0 ignored issues
show
Bug introduced by
$this->wsdl->namespaces of type array is incompatible with the type string expected by parameter $namespaces of nusoap_xmlschema::__construct(). ( Ignorable by Annotation )

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

4686
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', /** @scrutinizer ignore-type */ $this->wsdl->namespaces);
Loading history...
4687
        if ('document' == $style) {
4688
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4689
        }
4690
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4691
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = ['location' => '', 'loaded' => true];
4692
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = ['location' => '', 'loaded' => true];
4693
        $this->wsdl->bindings[$serviceName . 'Binding'] = [
4694
            'name' => $serviceName . 'Binding',
4695
            'style' => $style,
4696
            'transport' => $transport,
4697
            'portType' => $serviceName . 'PortType'
4698
        ];
4699
        $this->wsdl->ports[$serviceName . 'Port'] = [
4700
            'binding' => $serviceName . 'Binding',
4701
            'location' => $endpoint,
4702
            'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/'
4703
        ];
4704
    }
4705
}
4706
4707
/**
4708
 * Backward compatibility
4709
 */
4710
class soap_server extends nusoap_server
4711
{
4712
}
4713
4714
4715
/**
4716
 * parses a WSDL file, allows access to it's data, other utility methods.
4717
 * also builds WSDL structures programmatically.
4718
 *
4719
 * @author   Dietrich Ayala <[email protected]>
4720
 * @author   Scott Nichol <[email protected]>
4721
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
4722
 * @access public
4723
 */
4724
class wsdl extends nusoap_base
4725
{
4726
    // URL or filename of the root of this WSDL
4727
    public $wsdl;
4728
    // define internal arrays of bindings, ports, operations, messages, etc.
4729
    public $schemas = [];
4730
    public $currentSchema;
4731
    public $message = [];
4732
    public $complexTypes = [];
4733
    public $messages = [];
4734
    public $currentMessage;
4735
    public $currentOperation;
4736
    public $portTypes = [];
4737
    public $currentPortType;
4738
    public $bindings = [];
4739
    public $currentBinding;
4740
    public $ports = [];
4741
    public $currentPort;
4742
    public $opData = [];
4743
    public $status = '';
4744
    public $documentation = false;
4745
    public $endpoint = '';
4746
    // array of wsdl docs to import
4747
    public $import = [];
4748
    // parser vars
4749
    public $parser;
4750
    public $position = 0;
4751
    public $depth = 0;
4752
    public $depth_array = [];
4753
    // for getting wsdl
4754
    public $proxyhost = '';
4755
    public $proxyport = '';
4756
    public $proxyusername = '';
4757
    public $proxypassword = '';
4758
    public $timeout = 0;
4759
    public $response_timeout = 30;
4760
    public $curl_options = [];    // User-specified cURL options
4761
    public $use_curl = false;            // whether to always try to use cURL
4762
    // for HTTP authentication
4763
    public $username = '';                // Username for HTTP authentication
4764
    public $password = '';                // Password for HTTP authentication
4765
    public $authtype = '';                // Type of HTTP authentication
4766
    public $certRequest = [];        // Certificate for HTTP SSL authentication
4767
4768
    /**
4769
     * constructor
4770
     *
4771
     * @param string  $wsdl             WSDL document URL
4772
     * @param string $proxyhost
4773
     * @param string $proxyport
4774
     * @param string $proxyusername
4775
     * @param string $proxypassword
4776
     * @param integer $timeout          set the connection timeout
4777
     * @param integer $response_timeout set the response timeout
4778
     * @param array   $curl_options     user-specified cURL options
4779
     * @param boolean $use_curl         try to use cURL
4780
     * @access public
4781
     */
4782
    public function __construct($wsdl = '', $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $curl_options = null, $use_curl = false)
4783
    {
4784
        parent::__construct();
4785
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4786
        $this->proxyhost = $proxyhost;
4787
        $this->proxyport = $proxyport;
4788
        $this->proxyusername = $proxyusername;
4789
        $this->proxypassword = $proxypassword;
4790
        $this->timeout = $timeout;
4791
        $this->response_timeout = $response_timeout;
4792
        if (is_array($curl_options)) {
4793
            $this->curl_options = $curl_options;
4794
        }
4795
        $this->use_curl = $use_curl;
4796
        $this->fetchWSDL($wsdl);
4797
    }
4798
4799
    /**
4800
     * fetches the WSDL document and parses it
4801
     *
4802
     * @access public
4803
     */
4804
    public function fetchWSDL($wsdl)
4805
    {
4806
        $this->debug("parse and process WSDL path=$wsdl");
4807
        $this->wsdl = $wsdl;
4808
        // parse wsdl file
4809
        if ('' != $this->wsdl) {
4810
            $this->parseWSDL($this->wsdl);
4811
        }
4812
        // imports
4813
        // TODO: handle imports more properly, grabbing them in-line and nesting them
4814
        $imported_urls = [];
4815
        $imported = 1;
4816
        while ($imported > 0) {
4817
            $imported = 0;
4818
            // Schema imports
4819
            foreach ($this->schemas as $ns => $list) {
4820
                foreach ($list as $xs) {
4821
                    $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4822
                    foreach ($xs->imports as $ns2 => $list2) {
4823
                        for ($ii = 0, $iiMax = count($list2); $ii < $iiMax; $ii++) {
4824
                            if (!$list2[$ii]['loaded']) {
4825
                                $this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;
4826
                                $url = $list2[$ii]['location'];
4827
                                if ('' != $url) {
4828
                                    $urlparts = parse_url($url);
4829
                                    if (!isset($urlparts['host'])) {
4830
                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4831
                                            substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4832
                                    }
4833
                                    if (!in_array($url, $imported_urls)) {
4834
                                        $this->parseWSDL($url);
4835
                                        $imported++;
4836
                                        $imported_urls[] = $url;
4837
                                    }
4838
                                } else {
4839
                                    $this->debug('Unexpected scenario: empty URL for unloaded import');
4840
                                }
4841
                            }
4842
                        }
4843
                    }
4844
                }
4845
            }
4846
            // WSDL imports
4847
            $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4848
            foreach ($this->import as $ns => $list) {
4849
                for ($ii = 0, $iiMax = count($list); $ii < $iiMax; $ii++) {
4850
                    if (!$list[$ii]['loaded']) {
4851
                        $this->import[$ns][$ii]['loaded'] = true;
4852
                        $url = $list[$ii]['location'];
4853
                        if ('' != $url) {
4854
                            $urlparts = parse_url($url);
4855
                            if (!isset($urlparts['host'])) {
4856
                                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4857
                                    substr($wsdlparts['path'], 0, strrpos($wsdlparts['path'], '/') + 1) . $urlparts['path'];
4858
                            }
4859
                            if (!in_array($url, $imported_urls)) {
4860
                                $this->parseWSDL($url);
4861
                                $imported++;
4862
                                $imported_urls[] = $url;
4863
                            }
4864
                        } else {
4865
                            $this->debug('Unexpected scenario: empty URL for unloaded import');
4866
                        }
4867
                    }
4868
                }
4869
            }
4870
        }
4871
        // add new data to operation data
4872
        foreach ($this->bindings as $binding => $bindingData) {
4873
            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4874
                foreach ($bindingData['operations'] as $operation => $data) {
4875
                    $this->debug('post-parse data gathering for ' . $operation);
4876
                    $this->bindings[$binding]['operations'][$operation]['input'] =
4877
                        isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4878
                            array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[$bindingData['portType']][$operation]['input']) :
4879
                            $this->portTypes[$bindingData['portType']][$operation]['input'];
4880
                    $this->bindings[$binding]['operations'][$operation]['output'] =
4881
                        isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4882
                            array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[$bindingData['portType']][$operation]['output']) :
4883
                            $this->portTypes[$bindingData['portType']][$operation]['output'];
4884
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']])) {
4885
                        $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['input']['message']];
4886
                    }
4887
                    if (isset($this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']])) {
4888
                        $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[$this->bindings[$binding]['operations'][$operation]['output']['message']];
4889
                    }
4890
                    // Set operation style if necessary, but do not override one already provided
4891
                    if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4892
                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4893
                    }
4894
                    $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4895
                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[$bindingData['portType']][$operation]['documentation']) ? $this->portTypes[$bindingData['portType']][$operation]['documentation'] : '';
4896
                    $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4897
                }
4898
            }
4899
        }
4900
    }
4901
4902
    /**
4903
     * parses the wsdl document
4904
     *
4905
     * @param string $wsdl path or URL
4906
     * @access private
4907
     */
4908
    private function parseWSDL($wsdl = '')
4909
    {
4910
        $this->debug("parse WSDL at path=$wsdl");
4911
4912
        if ('' == $wsdl) {
4913
            $this->debug('no wsdl passed to parseWSDL()!!');
4914
            $this->setError('no wsdl passed to parseWSDL()!!');
4915
            return false;
4916
        }
4917
4918
        // parse $wsdl for url format
4919
        $wsdl_props = parse_url($wsdl);
4920
4921
        if (isset($wsdl_props['scheme']) && ('http' == $wsdl_props['scheme'] || 'https' == $wsdl_props['scheme'])) {
4922
            $this->debug('getting WSDL http(s) URL ' . $wsdl);
4923
            // get wsdl
4924
            $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4925
            $tr->request_method = 'GET';
4926
            $tr->useSOAPAction = false;
4927
            if ($this->proxyhost && $this->proxyport) {
4928
                $tr->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
0 ignored issues
show
Bug introduced by
It seems like $this->proxyhost can also be of type true; however, parameter $proxyhost of soap_transport_http::setProxy() 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

4928
                $tr->setProxy(/** @scrutinizer ignore-type */ $this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
Loading history...
Bug introduced by
It seems like $this->proxyport can also be of type true; however, parameter $proxyport of soap_transport_http::setProxy() 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

4928
                $tr->setProxy($this->proxyhost, /** @scrutinizer ignore-type */ $this->proxyport, $this->proxyusername, $this->proxypassword);
Loading history...
4929
            }
4930
            if ('' != $this->authtype) {
4931
                $tr->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
4932
            }
4933
            $tr->setEncoding('gzip, deflate');
4934
            $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4935
            //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4936
            //$this->debug("WSDL response\n" . $tr->incoming_payload);
4937
            $this->appendDebug($tr->getDebug());
4938
            // catch errors
4939
            if ($err = $tr->getError()) {
4940
                $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: ' . $err;
4941
                $this->debug($errstr);
4942
                $this->setError($errstr);
4943
                unset($tr);
4944
                return false;
4945
            }
4946
            unset($tr);
4947
            $this->debug('got WSDL URL');
4948
        } else {
4949
            // $wsdl is not http(s), so treat it as a file URL or plain file path
4950
            if (isset($wsdl_props['scheme']) && ('file' == $wsdl_props['scheme']) && isset($wsdl_props['path'])) {
4951
                $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4952
            } else {
4953
                $path = $wsdl;
4954
            }
4955
            $this->debug('getting WSDL file ' . $path);
4956
            if ($fp = @fopen($path, 'r')) {
4957
                $wsdl_string = '';
4958
                while ($data = fread($fp, 32768)) {
4959
                    $wsdl_string .= $data;
4960
                }
4961
                fclose($fp);
4962
            } else {
4963
                $errstr = "Bad path to WSDL file $path";
4964
                $this->debug($errstr);
4965
                $this->setError($errstr);
4966
                return false;
4967
            }
4968
        }
4969
        $this->debug('Parse WSDL');
4970
        // end new code added
4971
        // Create an XML parser.
4972
        $this->parser = xml_parser_create();
4973
        // Set the options for parsing the XML data.
4974
        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4975
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4976
        // Set the object for the parser.
4977
        xml_set_object($this->parser, $this);
4978
        // Set the element handlers for the parser.
4979
        xml_set_element_handler($this->parser, 'start_element', 'end_element');
4980
        xml_set_character_data_handler($this->parser, 'character_data');
4981
        // Parse the XML file.
4982
        if (!xml_parse($this->parser, $wsdl_string, true)) {
4983
            // Display an error message.
4984
            $errstr = sprintf(
4985
                'XML error parsing WSDL from %s on line %d: %s',
4986
                $wsdl,
4987
                xml_get_current_line_number($this->parser),
4988
                xml_error_string(xml_get_error_code($this->parser))
4989
            );
4990
            $this->debug($errstr);
4991
            $this->debug("XML payload:\n" . $wsdl_string);
4992
            $this->setError($errstr);
4993
            xml_parser_free($this->parser);
4994
            unset($this->parser);
4995
            return false;
4996
        }
4997
        // free the parser
4998
        xml_parser_free($this->parser);
4999
        unset($this->parser);
5000
        $this->debug('Parsing WSDL done');
5001
        // catch wsdl parse errors
5002
        if ($this->getError()) {
5003
            return false;
5004
        }
5005
        return true;
5006
    }
5007
5008
    /**
5009
     * start-element handler
5010
     *
5011
     * @param string $parser XML parser object
5012
     * @param string $name element name
5013
     * @param string|array $attrs associative array of attributes
5014
     * @access private
5015
     */
5016
    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...
5017
    {
5018
        if ('schema' == $this->status) {
5019
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
5020
            $this->appendDebug($this->currentSchema->getDebug());
5021
            $this->currentSchema->clearDebug();
5022
        } elseif (preg_match('/schema$/', $name)) {
5023
            $this->debug('Parsing WSDL schema');
5024
            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
5025
            $this->status = 'schema';
5026
            $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
0 ignored issues
show
Bug introduced by
$this->namespaces of type array is incompatible with the type string expected by parameter $namespaces of nusoap_xmlschema::__construct(). ( Ignorable by Annotation )

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

5026
            $this->currentSchema = new nusoap_xmlschema('', '', /** @scrutinizer ignore-type */ $this->namespaces);
Loading history...
5027
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
5028
            $this->appendDebug($this->currentSchema->getDebug());
5029
            $this->currentSchema->clearDebug();
5030
        } else {
5031
            // position in the total number of elements, starting from 0
5032
            $pos = $this->position++;
5033
            $depth = $this->depth++;
5034
            // set self as current value for this depth
5035
            $this->depth_array[$depth] = $pos;
5036
            $this->message[$pos] = ['cdata' => ''];
5037
            // process attributes
5038
            if (count($attrs) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $attrs can also be of type string; however, parameter $var of count() does only seem to accept Countable|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

5038
            if (count(/** @scrutinizer ignore-type */ $attrs) > 0) {
Loading history...
5039
                // register namespace declarations
5040
                foreach ($attrs as $k => $v) {
5041
                    if (preg_match('/^xmlns/', $k)) {
5042
                        if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
5043
                            $this->namespaces[$ns_prefix] = $v;
5044
                        } else {
5045
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
5046
                        }
5047
                        if ('http://www.w3.org/2001/XMLSchema' == $v || 'http://www.w3.org/1999/XMLSchema' == $v || 'http://www.w3.org/2000/10/XMLSchema' == $v) {
5048
                            $this->XMLSchemaVersion = $v;
5049
                            $this->namespaces['xsi'] = $v . '-instance';
5050
                        }
5051
                    }
5052
                }
5053
                // expand each attribute prefix to its namespace
5054
                foreach ($attrs as $k => $v) {
5055
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
5056
                    if ('location' != $k && 'soapAction' != $k && 'namespace' != $k) {
5057
                        $v = strpos($v, ':') ? $this->expandQname($v) : $v;
5058
                    }
5059
                    $eAttrs[$k] = $v;
5060
                }
5061
                $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 5054. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
5062
            } else {
5063
                $attrs = [];
5064
            }
5065
            // Set default prefix and namespace
5066
            // to prevent error Undefined variable $prefix and $namespace if (preg_match('/:/', $name)) return 0 or FALSE
5067
            $prefix = '';
5068
            $namespace = '';
5069
            // get element prefix, namespace and name
5070
            if (preg_match('/:/', $name)) {
5071
                // get ns prefix
5072
                $prefix = substr($name, 0, strpos($name, ':'));
5073
                // get ns
5074
                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
5075
                // get unqualified name
5076
                $name = substr(strstr($name, ':'), 1);
5077
            }
5078
            // process attributes, expanding any prefixes to namespaces
5079
            // find status, register data
5080
            switch ($this->status) {
5081
                case 'message':
5082
                    if ('part' == $name) {
5083
                        if (isset($attrs['type'])) {
5084
                            $this->debug('msg ' . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
0 ignored issues
show
Bug introduced by
It seems like $attrs can also be of type string; however, parameter $pieces of implode() 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

5084
                            $this->debug('msg ' . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', /** @scrutinizer ignore-type */ $attrs));
Loading history...
5085
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
5086
                        }
5087
                        if (isset($attrs['element'])) {
5088
                            $this->debug('msg ' . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
5089
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
5090
                        }
5091
                    }
5092
                    break;
5093
                case 'portType':
5094
                    switch ($name) {
5095
                        case 'operation':
5096
                            $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...
5097
                            $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
5098
                            if (isset($attrs['parameterOrder'])) {
5099
                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
5100
                            }
5101
                            break;
5102
                        case 'documentation':
5103
                            $this->documentation = true;
5104
                            break;
5105
                        // merge input/output data
5106
                        default:
5107
                            $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
5108
                            $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
5109
                            break;
5110
                    }
5111
                    break;
5112
                case 'binding':
5113
                    switch ($name) {
5114
                        case 'binding':
5115
                            // get ns prefix
5116
                            if (isset($attrs['style'])) {
5117
                                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
5118
                            }
5119
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
0 ignored issues
show
Bug introduced by
It seems like $attrs can also be of type string; however, parameter $array2 of array_merge() does only seem to accept null|array, maybe add an additional type check? ( Ignorable by Annotation )

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

5119
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], /** @scrutinizer ignore-type */ $attrs);
Loading history...
5120
                            break;
5121
                        case 'header':
5122
                            $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
5123
                            break;
5124
                        case 'operation':
5125
                            if (isset($attrs['soapAction'])) {
5126
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
5127
                            }
5128
                            if (isset($attrs['style'])) {
5129
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
5130
                            }
5131
                            if (isset($attrs['name'])) {
5132
                                $this->currentOperation = $attrs['name'];
5133
                                $this->debug("current binding operation: $this->currentOperation");
5134
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
5135
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
5136
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
5137
                            }
5138
                            break;
5139
                        case 'input':
5140
                            $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...
5141
                            break;
5142
                        case 'output':
5143
                            $this->opStatus = 'output';
5144
                            break;
5145
                        case 'body':
5146
                            if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5147
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5148
                            } else {
5149
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5150
                            }
5151
                            break;
5152
                    }
5153
                    break;
5154
                case 'service':
5155
                    switch ($name) {
5156
                        case 'port':
5157
                            $this->currentPort = $attrs['name'];
5158
                            $this->debug('current port: ' . $this->currentPort);
5159
                            $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5160
5161
                            break;
5162
                        case 'address':
5163
                            $this->ports[$this->currentPort]['location'] = $attrs['location'];
5164
                            $this->ports[$this->currentPort]['bindingType'] = $namespace;
5165
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['bindingType'] = $namespace;
5166
                            $this->bindings[$this->ports[$this->currentPort]['binding']]['endpoint'] = $attrs['location'];
5167
                            break;
5168
                    }
5169
                    break;
5170
            }
5171
            // set status
5172
            switch ($name) {
5173
                case 'import':
5174
                    if (isset($attrs['location'])) {
5175
                        $this->import[$attrs['namespace']][] = ['location' => $attrs['location'], 'loaded' => false];
5176
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]) . ')');
5177
                    } else {
5178
                        $this->import[$attrs['namespace']][] = ['location' => '', 'loaded' => true];
5179
                        if (!$this->getPrefixFromNamespace($attrs['namespace'])) {
5180
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $attrs['namespace'];
5181
                        }
5182
                        $this->debug('parsing import ' . $attrs['namespace'] . ' - [no location] (' . count($this->import[$attrs['namespace']]) . ')');
5183
                    }
5184
                    break;
5185
                //wait for schema
5186
                //case 'types':
5187
                //	$this->status = 'schema';
5188
                //	break;
5189
                case 'message':
5190
                    $this->status = 'message';
5191
                    $this->messages[$attrs['name']] = [];
5192
                    $this->currentMessage = $attrs['name'];
5193
                    break;
5194
                case 'portType':
5195
                    $this->status = 'portType';
5196
                    $this->portTypes[$attrs['name']] = [];
5197
                    $this->currentPortType = $attrs['name'];
5198
                    break;
5199
                case 'binding':
5200
                    if (isset($attrs['name'])) {
5201
                        // get binding name
5202
                        if (strpos($attrs['name'], ':')) {
5203
                            $this->currentBinding = $this->getLocalPart($attrs['name']);
5204
                        } else {
5205
                            $this->currentBinding = $attrs['name'];
5206
                        }
5207
                        $this->status = 'binding';
5208
                        $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5209
                        $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5210
                    }
5211
                    break;
5212
                case 'service':
5213
                    $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...
5214
                    $this->status = 'service';
5215
                    $this->debug('current service: ' . $this->serviceName);
5216
                    break;
5217
                case 'definitions':
5218
                    foreach ($attrs as $name => $value) {
0 ignored issues
show
introduced by
$name is overwriting one of the parameters of this function.
Loading history...
5219
                        $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...
5220
                    }
5221
                    break;
5222
            }
5223
        }
5224
    }
5225
5226
    /**
5227
     * end-element handler
5228
     *
5229
     * @param string $parser XML parser object
5230
     * @param string $name element name
5231
     * @access private
5232
     */
5233
    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...
5234
    {
5235
        // unset schema status
5236
        if (/*preg_match('/types$/', $name) ||*/
5237
        preg_match('/schema$/', $name)
5238
        ) {
5239
            $this->status = '';
5240
            $this->appendDebug($this->currentSchema->getDebug());
5241
            $this->currentSchema->clearDebug();
5242
            $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5243
            $this->debug('Parsing WSDL schema done');
5244
        }
5245
        if ('schema' == $this->status) {
5246
            $this->currentSchema->schemaEndElement($parser, $name);
5247
        } else {
5248
            // bring depth down a notch
5249
            $this->depth--;
5250
        }
5251
        // end documentation
5252
        if ($this->documentation) {
5253
            //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5254
            //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5255
            $this->documentation = false;
5256
        }
5257
    }
5258
5259
    /**
5260
     * element content handler
5261
     *
5262
     * @param string $parser XML parser object
5263
     * @param string $data element content
5264
     * @access private
5265
     */
5266
    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

5266
    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...
5267
    {
5268
        $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5269
        if (isset($this->message[$pos]['cdata'])) {
5270
            $this->message[$pos]['cdata'] .= $data;
5271
        }
5272
        if ($this->documentation) {
5273
            $this->documentation .= $data;
5274
        }
5275
    }
5276
5277
    /**
5278
     * if authenticating, set user credentials here
5279
     *
5280
     * @param    string $username
5281
     * @param    string $password
5282
     * @param    string $authtype (basic|digest|certificate|ntlm)
5283
     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5284
     * @access   public
5285
     */
5286
    public function setCredentials($username, $password, $authtype = 'basic', $certRequest = [])
5287
    {
5288
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5289
        $this->appendDebug($this->varDump($certRequest));
5290
        $this->username = $username;
5291
        $this->password = $password;
5292
        $this->authtype = $authtype;
5293
        $this->certRequest = $certRequest;
5294
    }
5295
5296
    public function getBindingData($binding)
5297
    {
5298
        if (is_array($this->bindings[$binding])) {
5299
            return $this->bindings[$binding];
5300
        }
5301
    }
5302
5303
    /**
5304
     * returns an assoc array of operation names => operation data
5305
     *
5306
     * @param string $portName WSDL port name
5307
     * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5308
     * @return array
5309
     * @access public
5310
     */
5311
    public function getOperations($portName = '', $bindingType = 'soap')
5312
    {
5313
        $ops = [];
5314
        if ('soap' == $bindingType) {
5315
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5316
        } elseif ('soap12' == $bindingType) {
5317
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5318
        } else {
5319
            $this->debug("getOperations bindingType $bindingType may not be supported");
5320
        }
5321
        $this->debug("getOperations for port '$portName' bindingType $bindingType");
5322
        // loop thru ports
5323
        foreach ($this->ports as $port => $portData) {
5324
            $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5325
            if ('' == $portName || $port == $portName) {
5326
                // binding type of port matches parameter
5327
                if ($portData['bindingType'] == $bindingType) {
5328
                    $this->debug("getOperations found port $port bindingType $bindingType");
5329
                    //$this->debug("port data: " . $this->varDump($portData));
5330
                    //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5331
                    // merge bindings
5332
                    if (isset($this->bindings[$portData['binding']]['operations'])) {
5333
                        $ops = array_merge($ops, $this->bindings[$portData['binding']]['operations']);
5334
                    }
5335
                }
5336
            }
5337
        }
5338
        if (0 == count($ops)) {
5339
            $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5340
        }
5341
        return $ops;
5342
    }
5343
5344
    /**
5345
     * returns an associative array of data necessary for calling an operation
5346
     *
5347
     * @param string $operation name of operation
5348
     * @param string $bindingType type of binding eg: soap, soap12
5349
     * @return array
5350
     * @access public
5351
     */
5352
    public function getOperationData($operation, $bindingType = 'soap')
5353
    {
5354
        if ('soap' == $bindingType) {
5355
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5356
        } elseif ('soap12' == $bindingType) {
5357
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5358
        }
5359
        // loop thru ports
5360
        foreach ($this->ports as $port => $portData) {
5361
            // binding type of port matches parameter
5362
            if ($portData['bindingType'] == $bindingType) {
5363
                // get binding
5364
                //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5365
                foreach (array_keys($this->bindings[$portData['binding']]['operations']) as $bOperation) {
5366
                    // note that we could/should also check the namespace here
5367
                    if ($operation == $bOperation) {
5368
                        $opData = $this->bindings[$portData['binding']]['operations'][$operation];
5369
                        return $opData;
5370
                    }
5371
                }
5372
            }
5373
        }
5374
    }
5375
5376
    /**
5377
     * returns an associative array of data necessary for calling an operation
5378
     *
5379
     * @param string $soapAction soapAction for operation
5380
     * @param string $bindingType type of binding eg: soap, soap12
5381
     * @return array
5382
     * @access public
5383
     */
5384
    public function getOperationDataForSoapAction($soapAction, $bindingType = 'soap')
5385
    {
5386
        if ('soap' == $bindingType) {
5387
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5388
        } elseif ('soap12' == $bindingType) {
5389
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5390
        }
5391
        // loop thru ports
5392
        foreach ($this->ports as $port => $portData) {
5393
            // binding type of port matches parameter
5394
            if ($portData['bindingType'] == $bindingType) {
5395
                // loop through operations for the binding
5396
                foreach ($this->bindings[$portData['binding']]['operations'] as $bOperation => $opData) {
5397
                    if ($opData['soapAction'] == $soapAction) {
5398
                        return $opData;
5399
                    }
5400
                }
5401
            }
5402
        }
5403
    }
5404
5405
    /**
5406
     * returns an array of information about a given type
5407
     * returns false if no type exists by the given name
5408
     *
5409
     *     typeDef = array(
5410
     *     'elements' => array(), // refs to elements array
5411
     *    'restrictionBase' => '',
5412
     *    'phpType' => '',
5413
     *    'order' => '(sequence|all)',
5414
     *    'attrs' => array() // refs to attributes array
5415
     *    )
5416
     *
5417
     * @param string $type the type
5418
     * @param string $ns namespace (not prefix) of the type
5419
     * @return mixed
5420
     * @access public
5421
     * @see nusoap_xmlschema
5422
     */
5423
    public function getTypeDef($type, $ns)
5424
    {
5425
        $this->debug("in getTypeDef: type=$type, ns=$ns");
5426
        if ((!$ns) && isset($this->namespaces['tns'])) {
5427
            $ns = $this->namespaces['tns'];
5428
            $this->debug("in getTypeDef: type namespace forced to $ns");
5429
        }
5430
        if (!isset($this->schemas[$ns])) {
5431
            foreach ($this->schemas as $ns0 => $schema0) {
5432
                if (0 == strcasecmp($ns, $ns0)) {
5433
                    $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5434
                    $ns = $ns0;
5435
                    break;
5436
                }
5437
            }
5438
        }
5439
        if (isset($this->schemas[$ns])) {
5440
            $this->debug("in getTypeDef: have schema for namespace $ns");
5441
            for ($i = 0, $iMax = count($this->schemas[$ns]); $i < $iMax; $i++) {
5442
                $xs = &$this->schemas[$ns][$i];
5443
                $t = $xs->getTypeDef($type);
5444
                $this->appendDebug($xs->getDebug());
5445
                $xs->clearDebug();
5446
                if ($t) {
5447
                    $this->debug("in getTypeDef: found type $type");
5448
                    if (!isset($t['phpType'])) {
5449
                        // get info for type to tack onto the element
5450
                        $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
5451
                        $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
5452
                        $etype = $this->getTypeDef($uqType, $ns);
5453
                        if ($etype) {
5454
                            $this->debug("found type for [element] $type:");
5455
                            $this->debug($this->varDump($etype));
5456
                            if (isset($etype['phpType'])) {
5457
                                $t['phpType'] = $etype['phpType'];
5458
                            }
5459
                            if (isset($etype['elements'])) {
5460
                                $t['elements'] = $etype['elements'];
5461
                            }
5462
                            if (isset($etype['attrs'])) {
5463
                                $t['attrs'] = $etype['attrs'];
5464
                            }
5465
                        } else {
5466
                            $this->debug("did not find type for [element] $type");
5467
                        }
5468
                    }
5469
                    return $t;
5470
                }
5471
            }
5472
            $this->debug("in getTypeDef: did not find type $type");
5473
        } else {
5474
            $this->debug("in getTypeDef: do not have schema for namespace $ns");
5475
        }
5476
        return false;
5477
    }
5478
5479
    /**
5480
     * prints html description of services
5481
     *
5482
     * @access private
5483
     */
5484
    public function webDescription()
5485
    {
5486
        global $HTTP_SERVER_VARS;
5487
5488
        if (isset($_SERVER)) {
5489
            $PHP_SELF = $_SERVER['PHP_SELF'];
5490
        } elseif (isset($HTTP_SERVER_VARS)) {
5491
            $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5492
        } else {
5493
            $this->setError('Neither _SERVER nor HTTP_SERVER_VARS is available');
5494
        }
5495
5496
        $b = '
5497
		<html><head><title>NuSOAP: ' . $this->serviceName . '</title>
5498
		<style type="text/css">
5499
		    body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5500
		    p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5501
		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5502
		    ul      { margin-top: 10px; margin-left: 20px; }
5503
		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
5504
		    .content{
5505
			margin-left: 0px; padding-bottom: 2em; }
5506
		    .nav {
5507
			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5508
			margin-top: 10px; margin-left: 0px; color: #000000;
5509
			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5510
		    .title {
5511
			font-family: arial; font-size: 26px; color: #ffffff;
5512
			background-color: #999999; width: 100%;
5513
			margin-left: 0px; margin-right: 0px;
5514
			padding-top: 10px; padding-bottom: 10px;}
5515
		    .hidden {
5516
			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5517
			font-family: arial; overflow: hidden; width: 600;
5518
			padding: 20px; font-size: 10px; background-color: #999999;
5519
			layer-background-color:#FFFFFF; }
5520
		    a,a:active  { color: charcoal; font-weight: bold; }
5521
		    a:visited   { color: #666666; font-weight: bold; }
5522
		    a:hover     { color: cc3300; font-weight: bold; }
5523
		</style>
5524
		<script language="JavaScript" type="text/javascript">
5525
		<!--
5526
		// POP-UP CAPTIONS...
5527
		function lib_bwcheck(){ //Browsercheck (needed)
5528
		    this.ver=navigator.appVersion
5529
		    this.agent=navigator.userAgent
5530
		    this.dom=document.getElementById?1:0
5531
		    this.opera5=this.agent.indexOf("Opera 5")>-1
5532
		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5533
		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5534
		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5535
		    this.ie=this.ie4||this.ie5||this.ie6
5536
		    this.mac=this.agent.indexOf("Mac")>-1
5537
		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5538
		    this.ns4=(document.layers && !this.dom)?1:0;
5539
		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5540
		    return this
5541
		}
5542
		var bw = new lib_bwcheck()
5543
		//Makes crossbrowser object.
5544
		function makeObj(obj){
5545
		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5546
		    if(!this.evnt) return false
5547
		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5548
		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5549
		    this.writeIt=b_writeIt;
5550
		    return this
5551
		}
5552
		// A unit of measure that will be added when setting the position of a layer.
5553
		//var px = bw.ns4||window.opera?"":"px";
5554
		function b_writeIt(text){
5555
		    if (bw.ns4){this.wref.write(text);this.wref.close()}
5556
		    else this.wref.innerHTML = text
5557
		}
5558
		//Shows the messages
5559
		var oDesc;
5560
		function popup(divid){
5561
		    if(oDesc = new makeObj(divid)){
5562
			oDesc.css.visibility = "visible"
5563
		    }
5564
		}
5565
		function popout(){ // Hides message
5566
		    if(oDesc) oDesc.css.visibility = "hidden"
5567
		}
5568
		//-->
5569
		</script>
5570
		</head>
5571
		<body>
5572
		<div class=content>
5573
			<br><br>
5574
			<div class=title>' . $this->serviceName . '</div>
5575
			<div class=nav>
5576
				<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...
5577
				Click on an operation name to view it&apos;s details.</p>
5578
				<ul>';
5579
        foreach ($this->getOperations() as $op => $data) {
5580
            $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5581
            // create hidden div
5582
            $b .= "<div id='$op' class='hidden'>
5583
				    <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
5584
            foreach ($data as $donnie => $marie) { // loop through opdata
5585
                if ('input' == $donnie || 'output' == $donnie) { // show input/output data
5586
                    $b .= "<font color='white'>" . ucfirst($donnie) . ':</font><br>';
5587
                    foreach ($marie as $captain => $tenille) { // loop through data
5588
                        if ('parts' == $captain) { // loop thru parts
5589
                            $b .= "&nbsp;&nbsp;$captain:<br>";
5590
                            //if(is_array($tenille)){
5591
                            foreach ($tenille as $joanie => $chachi) {
5592
                                $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
5593
                            }
5594
                            //}
5595
                        } else {
5596
                            $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
5597
                        }
5598
                    }
5599
                } else {
5600
                    $b .= "<font color='white'>" . ucfirst($donnie) . ":</font> $marie<br>";
5601
                }
5602
            }
5603
            $b .= '</div>';
5604
        }
5605
        $b .= '
5606
				<ul>
5607
			</div>
5608
		</div></body></html>';
5609
        return $b;
5610
    }
5611
5612
    /**
5613
     * serialize the parsed wsdl
5614
     *
5615
     * @param mixed $debug whether to put debug=1 in endpoint URL
5616
     * @return string serialization of WSDL
5617
     * @access public
5618
     */
5619
    public function serialize($debug = 0)
5620
    {
5621
        $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5622
        $xml .= "\n<definitions";
5623
        foreach ($this->namespaces as $k => $v) {
5624
            $xml .= " xmlns:$k=\"$v\"";
5625
        }
5626
        // 10.9.02 - add poulter fix for wsdl and tns declarations
5627
        if (isset($this->namespaces['wsdl'])) {
5628
            $xml .= ' xmlns="' . $this->namespaces['wsdl'] . '"';
5629
        }
5630
        if (isset($this->namespaces['tns'])) {
5631
            $xml .= ' targetNamespace="' . $this->namespaces['tns'] . '"';
5632
        }
5633
        $xml .= '>';
5634
        // imports
5635
        if (count($this->import) > 0) {
5636
            foreach ($this->import as $ns => $list) {
5637
                foreach ($list as $ii) {
5638
                    if ('' != $ii['location']) {
5639
                        $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
5640
                    } else {
5641
                        $xml .= '<import namespace="' . $ns . '" />';
5642
                    }
5643
                }
5644
            }
5645
        }
5646
        // types
5647
        if (count($this->schemas) >= 1) {
5648
            $xml .= "\n<types>\n";
5649
            foreach ($this->schemas as $ns => $list) {
5650
                foreach ($list as $xs) {
5651
                    $xml .= $xs->serializeSchema();
5652
                }
5653
            }
5654
            $xml .= '</types>';
5655
        }
5656
        // messages
5657
        if (count($this->messages) >= 1) {
5658
            foreach ($this->messages as $msgName => $msgParts) {
5659
                $xml .= "\n<message name=\"" . $msgName . '">';
5660
                if (is_array($msgParts)) {
5661
                    foreach ($msgParts as $partName => $partType) {
5662
                        // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5663
                        if (strpos($partType, ':')) {
5664
                            $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

5664
                            $typePrefix = $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($partType));
Loading history...
5665
                        } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5666
                            // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5667
                            $typePrefix = 'xsd';
5668
                        } else {
5669
                            foreach ($this->typemap as $ns => $types) {
5670
                                if (isset($types[$partType])) {
5671
                                    $typePrefix = $this->getPrefixFromNamespace($ns);
5672
                                }
5673
                            }
5674
                            if (!isset($typePrefix)) {
5675
                                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...
5676
                            }
5677
                        }
5678
                        $ns = $this->getNamespaceFromPrefix($typePrefix);
5679
                        $localPart = $this->getLocalPart($partType);
5680
                        $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

5680
                        $typeDef = $this->getTypeDef($localPart, /** @scrutinizer ignore-type */ $ns);
Loading history...
5681
                        if ('element' == $typeDef['typeClass']) {
5682
                            $elementortype = 'element';
5683
                            if ('^' == substr($localPart, -1)) {
5684
                                $localPart = substr($localPart, 0, -1);
5685
                            }
5686
                        } else {
5687
                            $elementortype = 'type';
5688
                        }
5689
                        $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
5690
                    }
5691
                }
5692
                $xml .= '</message>';
5693
            }
5694
        }
5695
        // bindings & porttypes
5696
        if (count($this->bindings) >= 1) {
5697
            $binding_xml = '';
5698
            $portType_xml = '';
5699
            foreach ($this->bindings as $bindingName => $attrs) {
5700
                $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5701
                $binding_xml .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
5702
                $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5703
                foreach ($attrs['operations'] as $opName => $opParts) {
5704
                    $binding_xml .= "\n" . '  <operation name="' . $opName . '">';
5705
                    $binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="' . $opParts['style'] . '"/>';
5706
                    if (isset($opParts['input']['encodingStyle']) && '' != $opParts['input']['encodingStyle']) {
5707
                        $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5708
                    } else {
5709
                        $enc_style = '';
5710
                    }
5711
                    $binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
5712
                    if (isset($opParts['output']['encodingStyle']) && '' != $opParts['output']['encodingStyle']) {
5713
                        $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5714
                    } else {
5715
                        $enc_style = '';
5716
                    }
5717
                    $binding_xml .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
5718
                    $binding_xml .= "\n" . '  </operation>';
5719
                    $portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
5720
                    if (isset($opParts['parameterOrder'])) {
5721
                        $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5722
                    }
5723
                    $portType_xml .= '>';
5724
                    if (isset($opParts['documentation']) && '' != $opParts['documentation']) {
5725
                        $portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5726
                    }
5727
                    $portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '"/>';
5728
                    $portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '"/>';
5729
                    $portType_xml .= "\n" . '  </operation>';
5730
                }
5731
                $portType_xml .= "\n" . '</portType>';
5732
                $binding_xml .= "\n" . '</binding>';
5733
            }
5734
            $xml .= $portType_xml . $binding_xml;
5735
        }
5736
        // services
5737
        $xml .= "\n<service name=\"" . $this->serviceName . '">';
5738
        if (count($this->ports) >= 1) {
5739
            foreach ($this->ports as $pName => $attrs) {
5740
                $xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5741
                $xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
5742
                $xml .= "\n" . '  </port>';
5743
            }
5744
        }
5745
        $xml .= "\n" . '</service>';
5746
        return $xml . "\n</definitions>";
5747
    }
5748
5749
    /**
5750
     * determine whether a set of parameters are unwrapped
5751
     * when they are expect to be wrapped, Microsoft-style.
5752
     *
5753
     * @param string $type the type (element name) of the wrapper
5754
     * @param array $parameters the parameter values for the SOAP call
5755
     * @return boolean whether they parameters are unwrapped (and should be wrapped)
5756
     * @access private
5757
     */
5758
    private function parametersMatchWrapped($type, &$parameters)
5759
    {
5760
        $this->debug("in parametersMatchWrapped type=$type, parameters=");
5761
        $this->appendDebug($this->varDump($parameters));
5762
5763
        // split type into namespace:unqualified-type
5764
        if (strpos($type, ':')) {
5765
            $uqType = substr($type, strrpos($type, ':') + 1);
5766
            $ns = substr($type, 0, strrpos($type, ':'));
5767
            $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5768
            if ($this->getNamespaceFromPrefix($ns)) {
5769
                $ns = $this->getNamespaceFromPrefix($ns);
5770
                $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5771
            }
5772
        } else {
5773
            // TODO: should the type be compared to types in XSD, and the namespace
5774
            // set to XSD if the type matches?
5775
            $this->debug("in parametersMatchWrapped: No namespace for type $type");
5776
            $ns = '';
5777
            $uqType = $type;
5778
        }
5779
5780
        // get the type information
5781
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5782
            $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5783
            return false;
5784
        }
5785
        $this->debug('in parametersMatchWrapped: found typeDef=');
5786
        $this->appendDebug($this->varDump($typeDef));
5787
        if ('^' == substr($uqType, -1)) {
5788
            $uqType = substr($uqType, 0, -1);
5789
        }
5790
        $phpType = $typeDef['phpType'];
5791
        $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5792
        $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5793
5794
        // we expect a complexType or element of complexType
5795
        if ('struct' != $phpType) {
5796
            $this->debug('in parametersMatchWrapped: not a struct');
5797
            return false;
5798
        }
5799
5800
        // see whether the parameter names match the elements
5801
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5802
            $elements = 0;
5803
            $matches = 0;
5804
            foreach ($typeDef['elements'] as $name => $attrs) {
5805
                if (isset($parameters[$name])) {
5806
                    $this->debug("in parametersMatchWrapped: have parameter named $name");
5807
                    $matches++;
5808
                } else {
5809
                    $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5810
                }
5811
                $elements++;
5812
            }
5813
5814
            $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5815
            if (0 == $matches) {
5816
                return false;
5817
            }
5818
            return true;
5819
        }
5820
5821
        // since there are no elements for the type, if the user passed no
5822
        // parameters, the parameters match wrapped.
5823
        $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5824
        return 0 == count($parameters);
5825
    }
5826
5827
    /**
5828
     * serialize PHP values according to a WSDL message definition
5829
     * contrary to the method name, this is not limited to RPC
5830
     *
5831
     * TODO
5832
     * - multi-ref serialization
5833
     * - validate PHP values against type definitions, return errors if invalid
5834
     *
5835
     * @param string $operation operation name
5836
     * @param string $direction (input|output)
5837
     * @param mixed $parameters parameter value(s)
5838
     * @param string $bindingType (soap|soap12)
5839
     * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5840
     * @access public
5841
     */
5842
    public function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap')
5843
    {
5844
        $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5845
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5846
5847
        if ('input' != $direction && 'output' != $direction) {
5848
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5849
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5850
            return false;
5851
        }
5852
        if (!$opData = $this->getOperationData($operation, $bindingType)) {
5853
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5854
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5855
            return false;
5856
        }
5857
        $this->debug('in serializeRPCParameters: opData:');
5858
        $this->appendDebug($this->varDump($opData));
5859
5860
        // Get encoding style for output and set to current
5861
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5862
        if (('input' == $direction) && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5863
            $encodingStyle = $opData['output']['encodingStyle'];
5864
            $enc_style = $encodingStyle;
0 ignored issues
show
Unused Code introduced by
The assignment to $enc_style is dead and can be removed.
Loading history...
5865
        }
5866
5867
        // set input params
5868
        $xml = '';
5869
        if (isset($opData[$direction]['parts']) && count($opData[$direction]['parts']) > 0) {
5870
            $parts = &$opData[$direction]['parts'];
5871
            $part_count = count($parts);
5872
            $style = $opData['style'];
5873
            $use = $opData[$direction]['use'];
5874
            $this->debug("have $part_count part(s) to serialize using $style/$use");
5875
            if (is_array($parameters)) {
5876
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5877
                $parameter_count = count($parameters);
5878
                $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5879
                // check for Microsoft-style wrapped parameters
5880
                if ('document' == $style && 'literal' == $use && 1 == $part_count && isset($parts['parameters'])) {
5881
                    $this->debug('check whether the caller has wrapped the parameters');
5882
                    if ('output' == $direction && 'arraySimple' == $parametersArrayType && 1 == $parameter_count) {
5883
                        // TODO: consider checking here for double-wrapping, when
5884
                        // service function wraps, then NuSOAP wraps again
5885
                        $this->debug("change simple array to associative with 'parameters' element");
5886
                        $parameters['parameters'] = $parameters[0];
5887
                        unset($parameters[0]);
5888
                    }
5889
                    if (('arrayStruct' == $parametersArrayType || 0 == $parameter_count) && !isset($parameters['parameters'])) {
5890
                        $this->debug('check whether caller\'s parameters match the wrapped ones');
5891
                        if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5892
                            $this->debug('wrap the parameters for the caller');
5893
                            $parameters = ['parameters' => $parameters];
5894
                            $parameter_count = 1;
0 ignored issues
show
Unused Code introduced by
The assignment to $parameter_count is dead and can be removed.
Loading history...
5895
                        }
5896
                    }
5897
                }
5898
                foreach ($parts as $name => $type) {
5899
                    $this->debug("serializing part $name of type $type");
5900
                    // Track encoding style
5901
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5902
                        $encodingStyle = $opData[$direction]['encodingStyle'];
5903
                        $enc_style = $encodingStyle;
5904
                    } else {
5905
                        $enc_style = false;
5906
                    }
5907
                    // NOTE: add error handling here
5908
                    // if serializeType returns false, then catch global error and fault
5909
                    if ('arraySimple' == $parametersArrayType) {
5910
                        $p = array_shift($parameters);
5911
                        $this->debug('calling serializeType w/indexed param');
5912
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5913
                    } elseif (isset($parameters[$name])) {
5914
                        $this->debug('calling serializeType w/named param');
5915
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5916
                    } else {
5917
                        // TODO: only send nillable
5918
                        $this->debug('calling serializeType w/null param');
5919
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5920
                    }
5921
                }
5922
            } else {
5923
                $this->debug('no parameters passed.');
5924
            }
5925
        }
5926
        $this->debug("serializeRPCParameters returning: $xml");
5927
        return $xml;
5928
    }
5929
5930
    /**
5931
     * serialize a PHP value according to a WSDL message definition
5932
     *
5933
     * TODO
5934
     * - multi-ref serialization
5935
     * - validate PHP values against type definitions, return errors if invalid
5936
     *
5937
     * @param string $operation operation name
5938
     * @param string $direction (input|output)
5939
     * @param mixed $parameters parameter value(s)
5940
     * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5941
     * @access public
5942
     * @deprecated
5943
     */
5944
    public function serializeParameters($operation, $direction, $parameters)
5945
    {
5946
        $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5947
        $this->appendDebug('parameters=' . $this->varDump($parameters));
5948
5949
        if ('input' != $direction && 'output' != $direction) {
5950
            $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5951
            $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5952
            return false;
5953
        }
5954
        if (!$opData = $this->getOperationData($operation)) {
5955
            $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5956
            $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5957
            return false;
5958
        }
5959
        $this->debug('opData:');
5960
        $this->appendDebug($this->varDump($opData));
5961
5962
        // Get encoding style for output and set to current
5963
        $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5964
        if (('input' == $direction) && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5965
            $encodingStyle = $opData['output']['encodingStyle'];
5966
            $enc_style = $encodingStyle;
0 ignored issues
show
Unused Code introduced by
The assignment to $enc_style is dead and can be removed.
Loading history...
5967
        }
5968
5969
        // set input params
5970
        $xml = '';
5971
        if (isset($opData[$direction]['parts']) && count($opData[$direction]['parts']) > 0) {
5972
            $use = $opData[$direction]['use'];
5973
            $this->debug("use=$use");
5974
            $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5975
            if (is_array($parameters)) {
5976
                $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5977
                $this->debug('have ' . $parametersArrayType . ' parameters');
5978
                foreach ($opData[$direction]['parts'] as $name => $type) {
5979
                    $this->debug('serializing part "' . $name . '" of type "' . $type . '"');
5980
                    // Track encoding style
5981
                    if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5982
                        $encodingStyle = $opData[$direction]['encodingStyle'];
5983
                        $enc_style = $encodingStyle;
5984
                    } else {
5985
                        $enc_style = false;
5986
                    }
5987
                    // NOTE: add error handling here
5988
                    // if serializeType returns false, then catch global error and fault
5989
                    if ('arraySimple' == $parametersArrayType) {
5990
                        $p = array_shift($parameters);
5991
                        $this->debug('calling serializeType w/indexed param');
5992
                        $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5993
                    } elseif (isset($parameters[$name])) {
5994
                        $this->debug('calling serializeType w/named param');
5995
                        $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5996
                    } else {
5997
                        // TODO: only send nillable
5998
                        $this->debug('calling serializeType w/null param');
5999
                        $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
6000
                    }
6001
                }
6002
            } else {
6003
                $this->debug('no parameters passed.');
6004
            }
6005
        }
6006
        $this->debug("serializeParameters returning: $xml");
6007
        return $xml;
6008
    }
6009
6010
    /**
6011
     * serializes a PHP value according a given type definition
6012
     *
6013
     * @param string  $name          name of value (part or element)
6014
     * @param string  $type          XML schema type of value (type or element)
6015
     * @param mixed   $value         a native PHP value (parameter value)
6016
     * @param string  $use           use for part (encoded|literal)
6017
     * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6018
     * @param boolean $unqualified   a kludge for what should be XML namespace form handling
6019
     * @return string value serialized as an XML string
6020
     * @access private
6021
     */
6022
    private function serializeType($name, $type, $value, $use = 'encoded', $encodingStyle = false, $unqualified = false)
6023
    {
6024
        $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? 'unqualified' : 'qualified'));
6025
        $this->appendDebug('value=' . $this->varDump($value));
6026
        if ('encoded' == $use && $encodingStyle) {
6027
            $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
6028
        }
6029
6030
        // if a soapval has been supplied, let its type override the WSDL
6031
        if (is_object($value) && 'soapval' == get_class($value)) {
6032
            if ($value->type_ns) {
6033
                $type = $value->type_ns . ':' . $value->type;
6034
                $forceType = true;
6035
                $this->debug("in serializeType: soapval overrides type to $type");
6036
            } elseif ($value->type) {
6037
                $type = $value->type;
6038
                $forceType = true;
6039
                $this->debug("in serializeType: soapval overrides type to $type");
6040
            } else {
6041
                $forceType = false;
6042
                $this->debug('in serializeType: soapval does not override type');
6043
            }
6044
            $attrs = $value->attributes;
6045
            $value = $value->value;
6046
            $this->debug("in serializeType: soapval overrides value to $value");
6047
            if ($attrs) {
6048
                if (!is_array($value)) {
6049
                    $value['!'] = $value;
6050
                }
6051
                foreach ($attrs as $n => $v) {
6052
                    $value['!' . $n] = $v;
6053
                }
6054
                $this->debug('in serializeType: soapval provides attributes');
6055
            }
6056
        } else {
6057
            $forceType = false;
6058
        }
6059
6060
        $xml = '';
6061
        if (strpos($type, ':')) {
6062
            $uqType = substr($type, strrpos($type, ':') + 1);
6063
            $ns = substr($type, 0, strrpos($type, ':'));
6064
            $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
6065
            if ($this->getNamespaceFromPrefix($ns)) {
6066
                $ns = $this->getNamespaceFromPrefix($ns);
6067
                $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
6068
            }
6069
6070
            if ($ns == $this->XMLSchemaVersion || 'http://schemas.xmlsoap.org/soap/encoding/' == $ns) {
6071
                $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
6072
                if ($unqualified && 'literal' == $use) {
6073
                    $elementNS = ' xmlns=""';
6074
                } else {
6075
                    $elementNS = '';
6076
                }
6077
                if (null === $value) {
6078
                    if ('literal' == $use) {
6079
                        // TODO: depends on minOccurs
6080
                        $xml = "<$name$elementNS/>";
6081
                    } else {
6082
                        // TODO: depends on nillable, which should be checked before calling this method
6083
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
0 ignored issues
show
Bug introduced by
Are you sure $this->getPrefixFromNamespace($ns) of type mixed|false can be used in concatenation? ( Ignorable by Annotation )

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

6083
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . /** @scrutinizer ignore-type */ $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
Loading history...
6084
                    }
6085
                    $this->debug("in serializeType: returning: $xml");
6086
                    return $xml;
6087
                }
6088
                if ('Array' == $uqType) {
6089
                    // JBoss/Axis does this sometimes
6090
                    return $this->serialize_val($value, $name, false, false, false, false, $use);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $name_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6090
                    return $this->serialize_val($value, $name, false, /** @scrutinizer ignore-type */ false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6090
                    return $this->serialize_val($value, $name, /** @scrutinizer ignore-type */ false, false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6090
                    return $this->serialize_val($value, $name, false, false, /** @scrutinizer ignore-type */ false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type array expected by parameter $attributes of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6090
                    return $this->serialize_val($value, $name, false, false, false, /** @scrutinizer ignore-type */ false, $use);
Loading history...
6091
                }
6092
                if ('boolean' == $uqType) {
6093
                    if ((is_string($value) && 'false' == $value) || (!$value)) {
6094
                        $value = 'false';
6095
                    } else {
6096
                        $value = 'true';
6097
                    }
6098
                }
6099
                if ('string' == $uqType && 'string' == gettype($value)) {
6100
                    $value = $this->expandEntities($value);
6101
                }
6102
                if (('long' == $uqType || 'unsignedLong' == $uqType) && 'double' == gettype($value)) {
6103
                    $value = sprintf('%.0lf', $value);
6104
                }
6105
                // it's a scalar
6106
                // TODO: what about null/nil values?
6107
                // check type isn't a custom type extending xmlschema namespace
6108
                if (!$this->getTypeDef($uqType, $ns)) {
6109
                    if ('literal' == $use) {
6110
                        if ($forceType) {
6111
                            $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6112
                        } else {
6113
                            $xml = "<$name$elementNS>$value</$name>";
6114
                        }
6115
                    } else {
6116
                        $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6117
                    }
6118
                    $this->debug("in serializeType: returning: $xml");
6119
                    return $xml;
6120
                }
6121
                $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
6122
            } elseif ('http://xml.apache.org/xml-soap' == $ns) {
6123
                $this->debug('in serializeType: appears to be Apache SOAP type');
6124
                if ('Map' == $uqType) {
6125
                    $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6126
                    if (!$tt_prefix) {
6127
                        $this->debug('in serializeType: Add namespace for Apache SOAP type');
6128
                        $tt_prefix = 'ns' . mt_rand(1000, 9999);
6129
                        $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
6130
                        // force this to be added to usedNamespaces
6131
                        $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
6132
                    }
6133
                    $contents = '';
6134
                    foreach ($value as $k => $v) {
6135
                        $this->debug("serializing map element: key $k, value $v");
6136
                        $contents .= '<item>';
6137
                        $contents .= $this->serialize_val($k, 'key', false, false, false, false, $use);
6138
                        $contents .= $this->serialize_val($v, 'value', false, false, false, false, $use);
6139
                        $contents .= '</item>';
6140
                    }
6141
                    if ('literal' == $use) {
6142
                        if ($forceType) {
6143
                            $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
6144
                        } else {
6145
                            $xml = "<$name>$contents</$name>";
6146
                        }
6147
                    } else {
6148
                        $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6149
                    }
6150
                    $this->debug("in serializeType: returning: $xml");
6151
                    return $xml;
6152
                }
6153
                $this->debug('in serializeType: Apache SOAP type, but only support Map');
6154
            }
6155
        } else {
6156
            // TODO: should the type be compared to types in XSD, and the namespace
6157
            // set to XSD if the type matches?
6158
            $this->debug("in serializeType: No namespace for type $type");
6159
            $ns = '';
6160
            $uqType = $type;
6161
        }
6162
        if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
6163
            $this->setError("$type ($uqType) is not a supported type.");
6164
            $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6165
            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...
6166
        } else {
6167
            $this->debug('in serializeType: found typeDef');
6168
            $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6169
            if ('^' == substr($uqType, -1)) {
6170
                $uqType = substr($uqType, 0, -1);
6171
            }
6172
        }
6173
        if (!isset($typeDef['phpType'])) {
6174
            $this->setError("$type ($uqType) has no phpType.");
6175
            $this->debug("in serializeType: $type ($uqType) has no phpType.");
6176
            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...
6177
        }
6178
        $phpType = $typeDef['phpType'];
6179
        $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''));
6180
        // if php type == struct, map value to the <all> element names
6181
        if ('struct' == $phpType) {
6182
            if (isset($typeDef['typeClass']) && 'element' == $typeDef['typeClass']) {
6183
                $elementName = $uqType;
6184
                if (isset($typeDef['form']) && ('qualified' == $typeDef['form'])) {
6185
                    $elementNS = " xmlns=\"$ns\"";
6186
                } else {
6187
                    $elementNS = ' xmlns=""';
6188
                }
6189
            } else {
6190
                $elementName = $name;
6191
                if ($unqualified) {
6192
                    $elementNS = ' xmlns=""';
6193
                } else {
6194
                    $elementNS = '';
6195
                }
6196
            }
6197
            if (null === $value) {
6198
                if ('literal' == $use) {
6199
                    // TODO: depends on minOccurs and nillable
6200
                    $xml = "<$elementName$elementNS/>";
6201
                } else {
6202
                    $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
6203
                }
6204
                $this->debug("in serializeType: returning: $xml");
6205
                return $xml;
6206
            }
6207
            if (is_object($value)) {
6208
                $value = get_object_vars($value);
6209
            }
6210
            if (is_array($value)) {
6211
                $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6212
                if ('literal' == $use) {
6213
                    if ($forceType) {
6214
                        $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6215
                    } else {
6216
                        $xml = "<$elementName$elementNS$elementAttrs>";
6217
                    }
6218
                } else {
6219
                    $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6220
                }
6221
6222
                if (isset($typeDef['simpleContent']) && 'true' == $typeDef['simpleContent']) {
6223
                    if (isset($value['!'])) {
6224
                        $xml .= $value['!'];
6225
                        $this->debug("in serializeType: serialized simpleContent for type $type");
6226
                    } else {
6227
                        $this->debug("in serializeType: no simpleContent to serialize for type $type");
6228
                    }
6229
                } else {
6230
                    // complexContent
6231
                    $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
0 ignored issues
show
Bug introduced by
It seems like $encodingStyle can also be of type false; however, parameter $encodingStyle of wsdl::serializeComplexTypeElements() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

6231
                    $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, /** @scrutinizer ignore-type */ $encodingStyle);
Loading history...
6232
                }
6233
                $xml .= "</$elementName>";
6234
            } else {
6235
                $this->debug('in serializeType: phpType is struct, but value is not an array');
6236
                $this->setError('phpType is struct, but value is not an array: see debug output for details');
6237
                $xml = '';
6238
            }
6239
        } elseif ('array' == $phpType) {
6240
            if (isset($typeDef['form']) && ('qualified' == $typeDef['form'])) {
6241
                $elementNS = " xmlns=\"$ns\"";
6242
            } else {
6243
                if ($unqualified) {
6244
                    $elementNS = ' xmlns=""';
6245
                } else {
6246
                    $elementNS = '';
6247
                }
6248
            }
6249
            if (null === $value) {
6250
                if ('literal' == $use) {
6251
                    // TODO: depends on minOccurs
6252
                    $xml = "<$name$elementNS/>";
6253
                } else {
6254
                    $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
6255
                        $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' .
0 ignored issues
show
Bug introduced by
Are you sure $this->getPrefixFromName...ap.org/soap/encoding/') of type mixed|false can be used in concatenation? ( Ignorable by Annotation )

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

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

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

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

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

6258
                        $this->getPrefixFromNamespace(/** @scrutinizer ignore-type */ $this->getPrefix($typeDef['arrayType'])) .
Loading history...
6259
                        ':' .
6260
                        $this->getLocalPart($typeDef['arrayType']) . '[0]"/>';
6261
                }
6262
                $this->debug("in serializeType: returning: $xml");
6263
                return $xml;
6264
            }
6265
            if (isset($typeDef['multidimensional'])) {
6266
                $nv = [];
6267
                foreach ($value as $v) {
6268
                    $cols = ',' . count($v);
6269
                    $nv = array_merge($nv, $v);
6270
                }
6271
                $value = $nv;
6272
            } else {
6273
                $cols = '';
6274
            }
6275
            if (is_array($value) && count($value) >= 1) {
6276
                $rows = count($value);
6277
                $contents = '';
6278
                foreach ($value as $k => $v) {
6279
                    $this->debug("serializing array element: $k, " . (is_array($v) ? 'array' : $v) . " of type: $typeDef[arrayType]");
6280
                    //if (strpos($typeDef['arrayType'], ':') ) {
6281
                    if (!in_array($typeDef['arrayType'], $this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6282
                        $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6283
                    } else {
6284
                        $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6285
                    }
6286
                }
6287
            } else {
6288
                $rows = 0;
6289
                $contents = null;
6290
            }
6291
            // TODO: for now, an empty value will be serialized as a zero element
6292
            // array.  Revisit this when coding the handling of null/nil values.
6293
            if ('literal' == $use) {
6294
                $xml = "<$name$elementNS>"
6295
                    . $contents
6296
                    . "</$name>";
6297
            } else {
6298
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . ':Array" ' .
6299
                    $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6300
                    . ':arrayType="'
6301
                    . $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6302
                       . ':' . $this->getLocalPart($typeDef['arrayType']) . "[$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...
6303
                    . $contents
6304
                    . "</$name>";
6305
            }
6306
        } elseif ('scalar' == $phpType) {
6307
            if (isset($typeDef['form']) && ('qualified' == $typeDef['form'])) {
6308
                $elementNS = " xmlns=\"$ns\"";
6309
            } else {
6310
                if ($unqualified) {
6311
                    $elementNS = ' xmlns=""';
6312
                } else {
6313
                    $elementNS = '';
6314
                }
6315
            }
6316
            if ('literal' == $use) {
6317
                if ($forceType) {
6318
                    $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6319
                } else {
6320
                    $xml = "<$name$elementNS>$value</$name>";
6321
                }
6322
            } else {
6323
                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6324
            }
6325
        }
6326
        $this->debug("in serializeType: returning: $xml");
6327
        return $xml;
6328
    }
6329
6330
    /**
6331
     * serializes the attributes for a complexType
6332
     *
6333
     * @param array $typeDef our internal representation of an XML schema type (or element)
6334
     * @param mixed $value a native PHP value (parameter value)
6335
     * @param string $ns the namespace of the type
6336
     * @param string $uqType the local part of the type
6337
     * @return string value serialized as an XML string
6338
     * @access private
6339
     */
6340
    private function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType)
6341
    {
6342
        $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6343
        $xml = '';
6344
        if (isset($typeDef['extensionBase'])) {
6345
            $nsx = $this->getPrefix($typeDef['extensionBase']);
6346
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6347
            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

6347
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6348
                $nsx = $this->getNamespaceFromPrefix($nsx);
6349
            }
6350
            if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6351
                $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6352
                $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6353
            } else {
6354
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6355
            }
6356
        }
6357
        if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6358
            $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6359
            if (is_array($value)) {
6360
                $xvalue = $value;
6361
            } elseif (is_object($value)) {
6362
                $xvalue = get_object_vars($value);
6363
            } else {
6364
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6365
                $xvalue = [];
6366
            }
6367
            foreach ($typeDef['attrs'] as $aName => $attrs) {
6368
                if (isset($xvalue['!' . $aName])) {
6369
                    $xname = '!' . $aName;
6370
                    $this->debug("value provided for attribute $aName with key $xname");
6371
                } elseif (isset($xvalue[$aName])) {
6372
                    $xname = $aName;
6373
                    $this->debug("value provided for attribute $aName with key $xname");
6374
                } elseif (isset($attrs['default'])) {
6375
                    $xname = '!' . $aName;
6376
                    $xvalue[$xname] = $attrs['default'];
6377
                    $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6378
                } else {
6379
                    $xname = '';
6380
                    $this->debug("no value provided for attribute $aName");
6381
                }
6382
                if ($xname) {
6383
                    $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . '"';
6384
                }
6385
            }
6386
        } else {
6387
            $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6388
        }
6389
        return $xml;
6390
    }
6391
6392
    /**
6393
     * serializes the elements for a complexType
6394
     *
6395
     * @param array  $typeDef       our internal representation of an XML schema type (or element)
6396
     * @param mixed  $value         a native PHP value (parameter value)
6397
     * @param string $ns            the namespace of the type
6398
     * @param string $uqType        the local part of the type
6399
     * @param string $use           use for part (encoded|literal)
6400
     * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6401
     * @return string value serialized as an XML string
6402
     * @access private
6403
     */
6404
    private function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use = 'encoded', $encodingStyle = false)
6405
    {
6406
        $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6407
        $xml = '';
6408
        if (isset($typeDef['extensionBase'])) {
6409
            $nsx = $this->getPrefix($typeDef['extensionBase']);
6410
            $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6411
            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

6411
            if ($this->getNamespaceFromPrefix(/** @scrutinizer ignore-type */ $nsx)) {
Loading history...
6412
                $nsx = $this->getNamespaceFromPrefix($nsx);
6413
            }
6414
            if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6415
                $this->debug("serialize elements for extension base $nsx:$uqTypex");
6416
                $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
0 ignored issues
show
Bug introduced by
It seems like $encodingStyle can also be of type false; however, parameter $encodingStyle of wsdl::serializeComplexTypeElements() 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

6416
                $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, /** @scrutinizer ignore-type */ $encodingStyle);
Loading history...
6417
            } else {
6418
                $this->debug("extension base $nsx:$uqTypex is not a supported type");
6419
            }
6420
        }
6421
        if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6422
            $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6423
            if (is_array($value)) {
6424
                $xvalue = $value;
6425
            } elseif (is_object($value)) {
6426
                $xvalue = get_object_vars($value);
6427
            } else {
6428
                $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6429
                $xvalue = [];
6430
            }
6431
            // toggle whether all elements are present - ideally should validate against schema
6432
            if (count($typeDef['elements']) != count($xvalue)) {
6433
                $optionals = true;
6434
            }
6435
            foreach ($typeDef['elements'] as $eName => $attrs) {
6436
                if (!isset($xvalue[$eName])) {
6437
                    if (isset($attrs['default'])) {
6438
                        $xvalue[$eName] = $attrs['default'];
6439
                        $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6440
                    }
6441
                }
6442
                // if user took advantage of a minOccurs=0, then only serialize named parameters
6443
                if (isset($optionals)
6444
                    && (!isset($xvalue[$eName]))
6445
                    && ((!isset($attrs['nillable'])) || 'true' != $attrs['nillable'])
6446
                ) {
6447
                    if (isset($attrs['minOccurs']) && '0' <> $attrs['minOccurs']) {
6448
                        $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6449
                    }
6450
                    // do nothing
6451
                    $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6452
                } else {
6453
                    // get value
6454
                    if (isset($xvalue[$eName])) {
6455
                        $v = $xvalue[$eName];
6456
                    } else {
6457
                        $v = null;
6458
                    }
6459
                    if (isset($attrs['form'])) {
6460
                        $unqualified = ('unqualified' == $attrs['form']);
6461
                    } else {
6462
                        $unqualified = false;
6463
                    }
6464
                    if (isset($attrs['maxOccurs']) && ('unbounded' == $attrs['maxOccurs'] || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && 'arraySimple' == $this->isArraySimpleOrStruct($v)) {
6465
                        $vv = $v;
6466
                        foreach ($vv as $k => $v) {
6467
                            if (isset($attrs['type']) || isset($attrs['ref'])) {
6468
                                // serialize schema-defined type
6469
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
0 ignored issues
show
Bug introduced by
It seems like $encodingStyle can also be of type false; however, parameter $encodingStyle of wsdl::serializeType() 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

6469
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, /** @scrutinizer ignore-type */ $encodingStyle, $unqualified);
Loading history...
6470
                            } else {
6471
                                // serialize generic type (can this ever really happen?)
6472
                                $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6473
                                $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type array expected by parameter $attributes of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6473
                                $xml .= $this->serialize_val($v, $eName, false, false, false, /** @scrutinizer ignore-type */ false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $name_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6473
                                $xml .= $this->serialize_val($v, $eName, false, /** @scrutinizer ignore-type */ false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6473
                                $xml .= $this->serialize_val($v, $eName, /** @scrutinizer ignore-type */ false, false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

6473
                                $xml .= $this->serialize_val($v, $eName, false, false, /** @scrutinizer ignore-type */ false, false, $use);
Loading history...
6474
                            }
6475
                        }
6476
                    } else {
6477
                        if (null === $v && isset($attrs['minOccurs']) && '0' == $attrs['minOccurs']) {
6478
                            // do nothing
6479
                        } elseif (null === $v && isset($attrs['nillable']) && 'true' == $attrs['nillable']) {
6480
                            // TODO: serialize a nil correctly, but for now serialize schema-defined type
6481
                            $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6482
                        } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
6483
                            // serialize schema-defined type
6484
                            $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6485
                        } else {
6486
                            // serialize generic type (can this ever really happen?)
6487
                            $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6488
                            $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6489
                        }
6490
                    }
6491
                }
6492
            }
6493
        } else {
6494
            $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6495
        }
6496
        return $xml;
6497
    }
6498
6499
    /**
6500
     * adds an XML Schema complex type to the WSDL types
6501
     *
6502
     * @param string $name
6503
     * @param string $typeClass (complexType|simpleType|attribute)
6504
     * @param string $phpType currently supported are array and struct (php assoc array)
6505
     * @param string $compositor (all|sequence|choice)
6506
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6507
     * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
6508
     * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6509
     * @param string $arrayType as namespace:name (xsd:string)
6510
     * @see nusoap_xmlschema
6511
     * @access public
6512
     */
6513
    public function addComplexType($name, $typeClass = 'complexType', $phpType = 'array', $compositor = '', $restrictionBase = '', $elements = [], $attrs = [], $arrayType = '')
6514
    {
6515
        if (count($elements) > 0) {
6516
            $eElements = [];
6517
            foreach ($elements as $n => $e) {
6518
                // expand each element
6519
                $ee = [];
6520
                foreach ($e as $k => $v) {
6521
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
6522
                    $v = strpos($v, ':') ? $this->expandQname($v) : $v;
6523
                    $ee[$k] = $v;
6524
                }
6525
                $eElements[$n] = $ee;
6526
            }
6527
            $elements = $eElements;
6528
        }
6529
6530
        if (count($attrs) > 0) {
6531
            foreach ($attrs as $n => $a) {
6532
                // expand each attribute
6533
                foreach ($a as $k => $v) {
6534
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
6535
                    $v = strpos($v, ':') ? $this->expandQname($v) : $v;
6536
                    $aa[$k] = $v;
6537
                }
6538
                $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...
6539
            }
6540
            $attrs = $eAttrs;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $eAttrs seems to be defined by a foreach iteration on line 6531. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
6541
        }
6542
6543
        $restrictionBase = strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6544
        $arrayType = strpos($arrayType, ':') ? $this->expandQname($arrayType) : $arrayType;
6545
6546
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6547
        $this->schemas[$typens][0]->addComplexType($name, $typeClass, $phpType, $compositor, $restrictionBase, $elements, $attrs, $arrayType);
6548
    }
6549
6550
    /**
6551
     * adds an XML Schema simple type to the WSDL types
6552
     *
6553
     * @param string $name
6554
     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6555
     * @param string $typeClass (should always be simpleType)
6556
     * @param string $phpType (should always be scalar)
6557
     * @param array $enumeration array of values
6558
     * @see nusoap_xmlschema
6559
     * @access public
6560
     */
6561
    public function addSimpleType($name, $restrictionBase = '', $typeClass = 'simpleType', $phpType = 'scalar', $enumeration = [])
6562
    {
6563
        $restrictionBase = strpos($restrictionBase, ':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6564
6565
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6566
        $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6567
    }
6568
6569
    /**
6570
     * adds an element to the WSDL types
6571
     *
6572
     * @param array $attrs attributes that must include name and type
6573
     * @see nusoap_xmlschema
6574
     * @access public
6575
     */
6576
    public function addElement($attrs)
6577
    {
6578
        $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6579
        $this->schemas[$typens][0]->addElement($attrs);
6580
    }
6581
6582
    /**
6583
     * register an operation with the server
6584
     *
6585
     * @param string $name          operation (method) name
6586
     * @param array $in assoc array of input values: key = param name, value = param type
6587
     * @param array $out assoc array of output values: key = param name, value = param type
6588
     * @param string $namespace optional The namespace for the operation
6589
     * @param string $soapaction optional The soapaction for the operation
6590
     * @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
6591
     * @param string $use           (encoded|literal) optional The use for the parameters (cannot mix right now)
6592
     * @param string $documentation optional The description to include in the WSDL
6593
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6594
     * @access public
6595
     */
6596
    public function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = '')
6597
    {
6598
        if ('encoded' == $use && '' == $encodingStyle) {
6599
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6600
        }
6601
6602
        if ('document' == $style) {
6603
            $elements = [];
6604
            foreach ($in as $n => $t) {
6605
                $elements[$n] = ['name' => $n, 'type' => $t, 'form' => 'unqualified'];
6606
            }
6607
            $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6608
            $this->addElement(['name' => $name, 'type' => $name . 'RequestType']);
6609
            $in = ['parameters' => 'tns:' . $name . '^'];
6610
6611
            $elements = [];
6612
            foreach ($out as $n => $t) {
6613
                $elements[$n] = ['name' => $n, 'type' => $t, 'form' => 'unqualified'];
6614
            }
6615
            $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6616
            $this->addElement(['name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified']);
6617
            $out = ['parameters' => 'tns:' . $name . 'Response' . '^'];
6618
        }
6619
6620
        // get binding
6621
        $this->bindings[$this->serviceName . 'Binding']['operations'][$name] =
6622
            [
6623
                'name' => $name,
6624
                'binding' => $this->serviceName . 'Binding',
6625
                'endpoint' => $this->endpoint,
6626
                'soapAction' => $soapaction,
6627
                'style' => $style,
6628
                'input' => [
6629
                    'use' => $use,
6630
                    'namespace' => $namespace,
6631
                    'encodingStyle' => $encodingStyle,
6632
                    'message' => $name . 'Request',
6633
                    'parts' => $in
6634
                ],
6635
                'output' => [
6636
                    'use' => $use,
6637
                    'namespace' => $namespace,
6638
                    'encodingStyle' => $encodingStyle,
6639
                    'message' => $name . 'Response',
6640
                    'parts' => $out
6641
                ],
6642
                'namespace' => $namespace,
6643
                'transport' => 'http://schemas.xmlsoap.org/soap/http',
6644
                'documentation' => $documentation
6645
            ];
6646
        // add portTypes
6647
        // add messages
6648
        if ($in) {
6649
            foreach ($in as $pName => $pType) {
6650
                if (strpos($pType, ':')) {
6651
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ':' . $this->getLocalPart($pType);
6652
                }
6653
                $this->messages[$name . 'Request'][$pName] = $pType;
6654
            }
6655
        } else {
6656
            $this->messages[$name . 'Request'] = '0';
6657
        }
6658
        if ($out) {
6659
            foreach ($out as $pName => $pType) {
6660
                if (strpos($pType, ':')) {
6661
                    $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)) . ':' . $this->getLocalPart($pType);
6662
                }
6663
                $this->messages[$name . 'Response'][$pName] = $pType;
6664
            }
6665
        } else {
6666
            $this->messages[$name . 'Response'] = '0';
6667
        }
6668
        return true;
6669
    }
6670
}
6671
6672
6673
/**
6674
 *
6675
 * nusoap_parser class parses SOAP XML messages into native PHP values
6676
 *
6677
 * @author   Dietrich Ayala <[email protected]>
6678
 * @author   Scott Nichol <[email protected]>
6679
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
6680
 * @access   public
6681
 */
6682
class nusoap_parser extends nusoap_base
6683
{
6684
    public $xml = '';
6685
    public $xml_encoding = '';
6686
    public $method = '';
6687
    public $root_struct = '';
6688
    public $root_struct_name = '';
6689
    public $root_struct_namespace = '';
6690
    public $root_header = '';
6691
    public $document = '';            // incoming SOAP body (text)
6692
    // determines where in the message we are (envelope,header,body,method)
6693
    public $status = '';
6694
    public $position = 0;
6695
    public $depth = 0;
6696
    public $default_namespace = '';
6697
//    public $namespaces = []; //Field 'namespaces' is already defined in \nusoap_base
6698
    public $message = [];
6699
    public $parent = '';
6700
    public $fault = false;
6701
    public $fault_code = '';
6702
    public $fault_str = '';
6703
    public $fault_detail = '';
6704
    public $depth_array = [];
6705
    public $debug_flag = true;
6706
    public $soapresponse = null;    // parsed SOAP Body
6707
    public $soapheader = null;        // parsed SOAP Header
6708
    public $responseHeaders = '';    // incoming SOAP headers (text)
6709
    public $body_position = 0;
6710
    // for multiref parsing:
6711
    // array of id => pos
6712
    public $ids = [];
6713
    // array of id => hrefs => pos
6714
    public $multirefs = [];
6715
    // toggle for auto-decoding element content
6716
    public $decode_utf8 = true;
6717
6718
    /**
6719
     * constructor that actually does the parsing
6720
     *
6721
     * @param    string $xml         SOAP message
6722
     * @param    string $encoding    character encoding scheme of message
6723
     * @param    string $method      method for which XML is parsed (unused?)
6724
     * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6725
     * @access   public
6726
     */
6727
    public function __construct($xml, $encoding = 'UTF-8', $method = '', $decode_utf8 = true)
6728
    {
6729
        parent::__construct();
6730
        $this->xml = $xml;
6731
        $this->xml_encoding = $encoding;
6732
        $this->method = $method;
6733
        $this->decode_utf8 = $decode_utf8;
6734
6735
        // Check whether content has been read.
6736
        if (!empty($xml)) {
6737
            // Check XML encoding
6738
            $pos_xml = strpos($xml, '<?xml');
6739
            if (false !== $pos_xml) {
6740
                $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6741
                if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6742
                    $xml_encoding = $res[1];
6743
                    if (strtoupper($xml_encoding) != $encoding) {
6744
                        $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6745
                        $this->debug($err);
6746
                        if ('ISO-8859-1' != $encoding || 'UTF-8' != strtoupper($xml_encoding)) {
6747
                            $this->setError($err);
6748
                            return;
6749
                        }
6750
                        // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6751
                    } else {
6752
                        $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6753
                    }
6754
                } else {
6755
                    $this->debug('No encoding specified in XML declaration');
6756
                }
6757
            } else {
6758
                $this->debug('No XML declaration');
6759
            }
6760
            $this->debug('Entering nusoap_parser(), length=' . strlen($xml) . ', encoding=' . $encoding);
6761
            // Create an XML parser - why not xml_parser_create_ns?
6762
            $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...
6763
            // Set the options for parsing the XML data.
6764
            //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6765
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6766
            xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6767
            // Set the object for the parser.
6768
            xml_set_object($this->parser, $this);
6769
            // Set the element handlers for the parser.
6770
            xml_set_element_handler($this->parser, 'start_element', 'end_element');
6771
            xml_set_character_data_handler($this->parser, 'character_data');
6772
6773
            // Parse the XML file.
6774
            if (!xml_parse($this->parser, $xml, true)) {
6775
                // Display an error message.
6776
                $err = sprintf(
6777
                    'XML error parsing SOAP payload on line %d: %s',
6778
                    xml_get_current_line_number($this->parser),
6779
                    xml_error_string(xml_get_error_code($this->parser))
6780
                );
6781
                $this->debug($err);
6782
                $this->debug("XML payload:\n" . $xml);
6783
                $this->setError($err);
6784
            } else {
6785
                $this->debug('in nusoap_parser ctor, message:');
6786
                $this->appendDebug($this->varDump($this->message));
6787
                $this->debug('parsed successfully, found root struct: ' . $this->root_struct . ' of name ' . $this->root_struct_name);
6788
                // get final value
6789
                $this->soapresponse = $this->message[$this->root_struct]['result'];
6790
                // get header value
6791
                if ('' != $this->root_header && isset($this->message[$this->root_header]['result'])) {
6792
                    $this->soapheader = $this->message[$this->root_header]['result'];
6793
                }
6794
                // resolve hrefs/ids
6795
                if (count($this->multirefs) > 0) {
6796
                    foreach ($this->multirefs as $id => $hrefs) {
6797
                        $this->debug('resolving multirefs for id: ' . $id);
6798
                        $idVal = $this->buildVal($this->ids[$id]);
6799
                        if (is_array($idVal) && isset($idVal['!id'])) {
6800
                            unset($idVal['!id']);
6801
                        }
6802
                        foreach ($hrefs as $refPos => $ref) {
6803
                            $this->debug('resolving href at pos ' . $refPos);
6804
                            $this->multirefs[$id][$refPos] = $idVal;
6805
                        }
6806
                    }
6807
                }
6808
            }
6809
            xml_parser_free($this->parser);
6810
            unset($this->parser);
6811
        } else {
6812
            $this->debug('xml was empty, didn\'t parse!');
6813
            $this->setError('xml was empty, didn\'t parse!');
6814
        }
6815
    }
6816
6817
    /**
6818
     * start-element handler
6819
     *
6820
     * @param    resource $parser XML parser object
6821
     * @param    string $name element name
6822
     * @param    array $attrs associative array of attributes
6823
     * @access   private
6824
     */
6825
    private function start_element($parser, $name, $attrs)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

Loading history...
6826
    {
6827
        // position in a total number of elements, starting from 0
6828
        // update class level pos
6829
        $pos = $this->position++;
6830
        // and set mine
6831
        $this->message[$pos] = ['pos' => $pos, 'children' => '', 'cdata' => ''];
6832
        // depth = how many levels removed from root?
6833
        // set mine as current global depth and increment global depth value
6834
        $this->message[$pos]['depth'] = $this->depth++;
6835
6836
        // else add self as child to whoever the current parent is
6837
        if (0 != $pos) {
6838
            $this->message[$this->parent]['children'] .= '|' . $pos;
6839
        }
6840
        // set my parent
6841
        $this->message[$pos]['parent'] = $this->parent;
6842
        // set self as current parent
6843
        $this->parent = $pos;
6844
        // set self as current value for this depth
6845
        $this->depth_array[$this->depth] = $pos;
6846
        // get element prefix
6847
        if (strpos($name, ':')) {
6848
            // get ns prefix
6849
            $prefix = substr($name, 0, strpos($name, ':'));
6850
            // get unqualified name
6851
            $name = substr(strstr($name, ':'), 1);
6852
        }
6853
        // set status
6854
        if ('Envelope' == $name && '' == $this->status) {
6855
            $this->status = 'envelope';
6856
        } elseif ('Header' == $name && 'envelope' == $this->status) {
6857
            $this->root_header = $pos;
6858
            $this->status = 'header';
6859
        } elseif ('Body' == $name && 'envelope' == $this->status) {
6860
            $this->status = 'body';
6861
            $this->body_position = $pos;
6862
        // set method
6863
        } elseif ('body' == $this->status && $pos == ($this->body_position + 1)) {
6864
            $this->status = 'method';
6865
            $this->root_struct_name = $name;
6866
            $this->root_struct = $pos;
6867
            $this->message[$pos]['type'] = 'struct';
6868
            $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6869
        }
6870
        // set my status
6871
        $this->message[$pos]['status'] = $this->status;
6872
        // set name
6873
        $this->message[$pos]['name'] = htmlspecialchars($name);
6874
        // set attrs
6875
        $this->message[$pos]['attrs'] = $attrs;
6876
6877
        // loop through atts, logging ns and type declarations
6878
        $attstr = '';
6879
        foreach ($attrs as $key => $value) {
6880
            $key_prefix = $this->getPrefix($key);
6881
            $key_localpart = $this->getLocalPart($key);
6882
            // if ns declarations, add to class level array of valid namespaces
6883
            if ('xmlns' == $key_prefix) {
6884
                if (preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/', $value)) {
6885
                    $this->XMLSchemaVersion = $value;
6886
                    $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6887
                    $this->namespaces['xsi'] = $this->XMLSchemaVersion . '-instance';
6888
                }
6889
                $this->namespaces[$key_localpart] = $value;
6890
                // set method namespace
6891
                if ($name == $this->root_struct_name) {
6892
                    $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...
6893
                }
6894
                // if it's a type declaration, set type
6895
            } elseif ('type' == $key_localpart) {
6896
                if (isset($this->message[$pos]['type']) && 'array' == $this->message[$pos]['type']) {
6897
                    // do nothing: already processed arrayType
6898
                } else {
6899
                    $value_prefix = $this->getPrefix($value);
6900
                    $value_localpart = $this->getLocalPart($value);
6901
                    $this->message[$pos]['type'] = $value_localpart;
6902
                    $this->message[$pos]['typePrefix'] = $value_prefix;
6903
                    if (isset($this->namespaces[$value_prefix])) {
6904
                        $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6905
                    } 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

6905
                    } elseif (isset($attrs['xmlns:' . /** @scrutinizer ignore-type */ $value_prefix])) {
Loading history...
6906
                        $this->message[$pos]['type_namespace'] = $attrs['xmlns:' . $value_prefix];
6907
                    }
6908
                    // should do something here with the namespace of specified type?
6909
                }
6910
            } elseif ('arrayType' == $key_localpart) {
6911
                $this->message[$pos]['type'] = 'array';
6912
                /* do arrayType ereg here
6913
                [1]    arrayTypeValue    ::=    atype asize
6914
                [2]    atype    ::=    QName rank*
6915
                [3]    rank    ::=    '[' (',')* ']'
6916
                [4]    asize    ::=    '[' length~ ']'
6917
                [5]    length    ::=    nextDimension* Digit+
6918
                [6]    nextDimension    ::=    Digit+ ','
6919
                */
6920
                $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
6921
                if (preg_match($expr, $value, $regs)) {
6922
                    $this->message[$pos]['typePrefix'] = $regs[1];
6923
                    $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6924
                    if (isset($this->namespaces[$regs[1]])) {
6925
                        $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6926
                    } elseif (isset($attrs['xmlns:' . $regs[1]])) {
6927
                        $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:' . $regs[1]];
6928
                    }
6929
                    $this->message[$pos]['arrayType'] = $regs[2];
6930
                    $this->message[$pos]['arraySize'] = $regs[3];
6931
                    $this->message[$pos]['arrayCols'] = $regs[4];
6932
                }
6933
                // specifies nil value (or not)
6934
            } elseif ('nil' == $key_localpart) {
6935
                $this->message[$pos]['nil'] = ('true' == $value || '1' == $value);
6936
            // some other attribute
6937
            } elseif ('href' != $key && 'xmlns' != $key && 'encodingStyle' != $key_localpart && 'root' != $key_localpart) {
6938
                $this->message[$pos]['xattrs']['!' . $key] = $value;
6939
            }
6940
6941
            if ('xmlns' == $key) {
6942
                $this->default_namespace = $value;
6943
            }
6944
            // log id
6945
            if ('id' == $key) {
6946
                $this->ids[$value] = $pos;
6947
            }
6948
            // root
6949
            if ('root' == $key_localpart && 1 == $value) {
6950
                $this->status = 'method';
6951
                $this->root_struct_name = $name;
6952
                $this->root_struct = $pos;
6953
                $this->debug("found root struct $this->root_struct_name, pos $pos");
6954
            }
6955
            // for doclit
6956
            $attstr .= " $key=\"$value\"";
6957
        }
6958
        // get namespace - must be done after namespace atts are processed
6959
        if (isset($prefix)) {
6960
            $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6961
            $this->default_namespace = $this->namespaces[$prefix];
6962
        } else {
6963
            $this->message[$pos]['namespace'] = $this->default_namespace;
6964
        }
6965
        if ('header' == $this->status) {
6966
            if ($this->root_header != $pos) {
6967
                $this->responseHeaders .= '<' . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6968
            }
6969
        } elseif ('' != $this->root_struct_name) {
6970
            $this->document .= '<' . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6971
        }
6972
    }
6973
6974
    /**
6975
     * end-element handler
6976
     *
6977
     * @param    resource $parser XML parser object
6978
     * @param    string $name element name
6979
     * @access   private
6980
     */
6981
    private function end_element($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

6981
    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...
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...
6982
    {
6983
        // position of current element is equal to the last value left in depth_array for my depth
6984
        $pos = $this->depth_array[$this->depth--];
6985
6986
        // get element prefix
6987
        if (strpos($name, ':')) {
6988
            // get ns prefix
6989
            $prefix = substr($name, 0, strpos($name, ':'));
6990
            // get unqualified name
6991
            $name = substr(strstr($name, ':'), 1);
6992
        }
6993
6994
        // build to native type
6995
        if (isset($this->body_position) && $pos > $this->body_position) {
6996
            // deal w/ multirefs
6997
            if (isset($this->message[$pos]['attrs']['href'])) {
6998
                // get id
6999
                $id = substr($this->message[$pos]['attrs']['href'], 1);
7000
                // add placeholder to href array
7001
                $this->multirefs[$id][$pos] = 'placeholder';
7002
                // add set a reference to it as the result value
7003
                $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
7004
            // build complexType values
7005
            } elseif ('' != $this->message[$pos]['children']) {
7006
                // if result has already been generated (struct/array)
7007
                if (!isset($this->message[$pos]['result'])) {
7008
                    $this->message[$pos]['result'] = $this->buildVal($pos);
7009
                }
7010
                // build complexType values of attributes and possibly simpleContent
7011
            } elseif (isset($this->message[$pos]['xattrs'])) {
7012
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
7013
                    $this->message[$pos]['xattrs']['!'] = null;
7014
                } elseif (isset($this->message[$pos]['cdata']) && '' != trim($this->message[$pos]['cdata'])) {
7015
                    if (isset($this->message[$pos]['type'])) {
7016
                        $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'] : '');
7017
                    } else {
7018
                        $parent = $this->message[$pos]['parent'];
7019
                        if (isset($this->message[$parent]['type']) && ('array' == $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7020
                            $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7021
                        } else {
7022
                            $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
7023
                        }
7024
                    }
7025
                }
7026
                $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
7027
            // set value of simpleType (or nil complexType)
7028
            } else {
7029
                //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
7030
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
7031
                    $this->message[$pos]['xattrs']['!'] = null;
7032
                } elseif (isset($this->message[$pos]['type'])) {
7033
                    $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'] : '');
7034
                } else {
7035
                    $parent = $this->message[$pos]['parent'];
7036
                    if (isset($this->message[$parent]['type']) && ('array' == $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7037
                        $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7038
                    } else {
7039
                        $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
7040
                    }
7041
                }
7042
7043
                /* add value to parent's result, if parent is struct/array
7044
                $parent = $this->message[$pos]['parent'];
7045
                if($this->message[$parent]['type'] != 'map'){
7046
                    if(strtolower($this->message[$parent]['type']) == 'array'){
7047
                        $this->message[$parent]['result'][] = $this->message[$pos]['result'];
7048
                    } else {
7049
                        $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
7050
                    }
7051
                }
7052
                */
7053
            }
7054
        }
7055
7056
        // for doclit
7057
        if ('header' == $this->status) {
7058
            if ($this->root_header != $pos) {
7059
                $this->responseHeaders .= '</' . (isset($prefix) ? $prefix . ':' : '') . "$name>";
7060
            }
7061
        } elseif ($pos >= $this->root_struct) {
7062
            $this->document .= '</' . (isset($prefix) ? $prefix . ':' : '') . "$name>";
7063
        }
7064
        // switch status
7065
        if ($pos == $this->root_struct) {
7066
            $this->status = 'body';
7067
            $this->root_struct_namespace = $this->message[$pos]['namespace'];
7068
        } elseif ($pos == $this->root_header) {
7069
            $this->status = 'envelope';
7070
        } elseif ('Body' == $name && 'body' == $this->status) {
7071
            $this->status = 'envelope';
7072
        } elseif ('Header' == $name && 'header' == $this->status) { // will never happen
7073
            $this->status = 'envelope';
7074
        } elseif ('Envelope' == $name && 'envelope' == $this->status) {
7075
            $this->status = '';
7076
        }
7077
        // set parent back to my parent
7078
        $this->parent = $this->message[$pos]['parent'];
7079
    }
7080
7081
    /**
7082
     * element content handler
7083
     *
7084
     * @param    resource $parser XML parser object
7085
     * @param    string $data element content
7086
     * @access   private
7087
     */
7088
    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

7088
    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...
7089
    {
7090
        $pos = $this->depth_array[$this->depth];
7091
        if ('UTF-8' == $this->xml_encoding) {
7092
            // TODO: add an option to disable this for folks who want
7093
            // raw UTF-8 that, e.g., might not map to iso-8859-1
7094
            // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
7095
            if ($this->decode_utf8) {
7096
                $data = utf8_decode($data);
7097
            }
7098
        }
7099
        $this->message[$pos]['cdata'] .= $data;
7100
        // for doclit
7101
        if ('header' == $this->status) {
7102
            $this->responseHeaders .= $data;
7103
        } else {
7104
            $this->document .= $data;
7105
        }
7106
    }
7107
7108
    /**
7109
     * get the parsed message (SOAP Body)
7110
     *
7111
     * @return    mixed
7112
     * @access   public
7113
     * @deprecated    use get_soapbody instead
7114
     */
7115
    public function get_response()
7116
    {
7117
        return $this->soapresponse;
7118
    }
7119
7120
    /**
7121
     * get the parsed SOAP Body (null if there was none)
7122
     *
7123
     * @return    mixed
7124
     * @access   public
7125
     */
7126
    public function get_soapbody()
7127
    {
7128
        return $this->soapresponse;
7129
    }
7130
7131
    /**
7132
     * get the parsed SOAP Header (null if there was none)
7133
     *
7134
     * @return    mixed
7135
     * @access   public
7136
     */
7137
    public function get_soapheader()
7138
    {
7139
        return $this->soapheader;
7140
    }
7141
7142
    /**
7143
     * get the unparsed SOAP Header
7144
     *
7145
     * @return    string XML or empty if no Header
7146
     * @access   public
7147
     */
7148
    public function getHeaders()
7149
    {
7150
        return $this->responseHeaders;
7151
    }
7152
7153
    /**
7154
     * decodes simple types into PHP variables
7155
     *
7156
     * @param    string $value value to decode
7157
     * @param    string $type XML type to decode
7158
     * @param    string $typens XML type namespace to decode
7159
     * @return    mixed PHP value
7160
     * @access   private
7161
     */
7162
    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

7162
    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...
7163
    {
7164
        // TODO: use the namespace!
7165
        if ((!isset($type)) || 'string' == $type || 'long' == $type || 'unsignedLong' == $type) {
7166
            return (string) $value;
7167
        }
7168
        if ('int' == $type || 'integer' == $type || 'short' == $type || 'byte' == $type) {
7169
            return (int) $value;
7170
        }
7171
        if ('float' == $type || 'double' == $type || 'decimal' == $type) {
7172
            return (double) $value;
7173
        }
7174
        if ('boolean' == $type) {
7175
            if ('false' == strtolower($value) || 'f' == strtolower($value)) {
7176
                return false;
7177
            }
7178
            return (boolean) $value;
7179
        }
7180
        if ('base64' == $type || 'base64Binary' == $type) {
7181
            $this->debug('Decode base64 value');
7182
            return base64_decode($value);
7183
        }
7184
        // obscure numeric types
7185
        if ('nonPositiveInteger' == $type || 'negativeInteger' == $type
7186
            || 'nonNegativeInteger' == $type
7187
            || 'positiveInteger' == $type
7188
            || 'unsignedInt' == $type
7189
            || 'unsignedShort' == $type
7190
            || 'unsignedByte' == $type
7191
        ) {
7192
            return (int) $value;
7193
        }
7194
        // bogus: parser treats array with no elements as a simple type
7195
        if ('array' == $type) {
7196
            return [];
7197
        }
7198
        // everything else
7199
        return (string) $value;
7200
    }
7201
7202
    /**
7203
     * builds response structures for compound values (arrays/structs)
7204
     * and scalars
7205
     *
7206
     * @param    integer $pos position in node tree
7207
     * @return    mixed    PHP value
7208
     * @access   private
7209
     */
7210
    private function buildVal($pos)
7211
    {
7212
        if (!isset($this->message[$pos]['type'])) {
7213
            $this->message[$pos]['type'] = '';
7214
        }
7215
        $this->debug('in buildVal() for ' . $this->message[$pos]['name'] . "(pos $pos) of type " . $this->message[$pos]['type']);
7216
        // if there are children...
7217
        if ('' != $this->message[$pos]['children']) {
7218
            $this->debug('in buildVal, there are children');
7219
            $children = explode('|', $this->message[$pos]['children']);
7220
            array_shift($children); // knock off empty
7221
            // md array
7222
            if (isset($this->message[$pos]['arrayCols']) && '' != $this->message[$pos]['arrayCols']) {
7223
                $r = 0; // rowcount
7224
                $c = 0; // colcount
7225
                foreach ($children as $child_pos) {
7226
                    $this->debug("in buildVal, got an MD array element: $r, $c");
7227
                    $params[$r][] = $this->message[$child_pos]['result'];
7228
                    $c++;
7229
                    if ($c == $this->message[$pos]['arrayCols']) {
7230
                        $c = 0;
7231
                        $r++;
7232
                    }
7233
                }
7234
                // array
7235
            } elseif ('array' == $this->message[$pos]['type'] || 'Array' == $this->message[$pos]['type']) {
7236
                $this->debug('in buildVal, adding array ' . $this->message[$pos]['name']);
7237
                foreach ($children as $child_pos) {
7238
                    $params[] = &$this->message[$child_pos]['result'];
7239
                }
7240
                // apache Map type: java hashtable
7241
            } elseif ('Map' == $this->message[$pos]['type'] && 'http://xml.apache.org/xml-soap' == $this->message[$pos]['type_namespace']) {
7242
                $this->debug('in buildVal, Java Map ' . $this->message[$pos]['name']);
7243
                foreach ($children as $child_pos) {
7244
                    $kv = explode('|', $this->message[$child_pos]['children']);
7245
                    $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
7246
                }
7247
                // generic compound type
7248
                //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
7249
            } else {
7250
                // Apache Vector type: treat as an array
7251
                $this->debug('in buildVal, adding Java Vector or generic compound type ' . $this->message[$pos]['name']);
7252
                if ('Vector' == $this->message[$pos]['type'] && 'http://xml.apache.org/xml-soap' == $this->message[$pos]['type_namespace']) {
7253
                    $notstruct = 1;
7254
                } else {
7255
                    $notstruct = 0;
7256
                }
7257
                //
7258
                foreach ($children as $child_pos) {
7259
                    if ($notstruct) {
7260
                        $params[] = &$this->message[$child_pos]['result'];
7261
                    } else {
7262
                        if (isset($params[$this->message[$child_pos]['name']])) {
7263
                            // de-serialize repeated element name into an array
7264
                            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 7260. Are you sure it is defined here?
Loading history...
7265
                                $params[$this->message[$child_pos]['name']] = [$params[$this->message[$child_pos]['name']]];
7266
                            }
7267
                            $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
7268
                        } else {
7269
                            $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
7270
                        }
7271
                    }
7272
                }
7273
            }
7274
            if (isset($this->message[$pos]['xattrs'])) {
7275
                $this->debug('in buildVal, handling attributes');
7276
                foreach ($this->message[$pos]['xattrs'] as $n => $v) {
7277
                    $params[$n] = $v;
7278
                }
7279
            }
7280
            // handle simpleContent
7281
            if (isset($this->message[$pos]['cdata']) && '' != trim($this->message[$pos]['cdata'])) {
7282
                $this->debug('in buildVal, handling simpleContent');
7283
                if (isset($this->message[$pos]['type'])) {
7284
                    $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7285
                } else {
7286
                    $parent = $this->message[$pos]['parent'];
7287
                    if (isset($this->message[$parent]['type']) && ('array' == $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7288
                        $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7289
                    } else {
7290
                        $params['!'] = $this->message[$pos]['cdata'];
7291
                    }
7292
                }
7293
            }
7294
            $ret = is_array($params) ? $params : [];
7295
            $this->debug('in buildVal, return:');
7296
            $this->appendDebug($this->varDump($ret));
7297
            return $ret;
7298
        } else {
7299
            $this->debug('in buildVal, no children, building scalar');
7300
            $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
7301
            if (isset($this->message[$pos]['type'])) {
7302
                $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7303
                $this->debug("in buildVal, return: $ret");
7304
                return $ret;
7305
            }
7306
            $parent = $this->message[$pos]['parent'];
7307
            if (isset($this->message[$parent]['type']) && ('array' == $this->message[$parent]['type']) && isset($this->message[$parent]['arrayType'])) {
7308
                $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7309
                $this->debug("in buildVal, return: $ret");
7310
                return $ret;
7311
            }
7312
            $ret = $this->message[$pos]['cdata'];
7313
            $this->debug("in buildVal, return: $ret");
7314
            return $ret;
7315
        }
7316
    }
7317
}
7318
7319
7320
/**
7321
 * Backward compatibility
7322
 */
7323
class soap_parser extends nusoap_parser
7324
{
7325
}
7326
7327
7328
/**
7329
 *
7330
 * [nu]soapclient higher level class for easy usage.
7331
 *
7332
 * usage:
7333
 *
7334
 * // instantiate client with server info
7335
 * $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
7336
 *
7337
 * // call method, get results
7338
 * echo $soapclient->call( string methodname [ ,array parameters] );
7339
 *
7340
 * // bye bye client
7341
 * unset($soapclient);
7342
 *
7343
 * @author   Dietrich Ayala <[email protected]>
7344
 * @author   Scott Nichol <[email protected]>
7345
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
7346
 * @access   public
7347
 */
7348
class nusoap_client extends nusoap_base
7349
{
7350
    public $username = '';                // Username for HTTP authentication
7351
    public $password = '';                // Password for HTTP authentication
7352
    public $authtype = '';                // Type of HTTP authentication
7353
    public $certRequest = [];        // Certificate for HTTP SSL authentication
7354
    public $requestHeaders = false;    // SOAP headers in request (text)
7355
    public $responseHeaders = '';        // SOAP headers from response (incomplete namespace resolution) (text)
7356
    public $responseHeader = null;        // SOAP Header from response (parsed)
7357
    public $document = '';                // SOAP body response portion (incomplete namespace resolution) (text)
7358
    public $endpoint;
7359
    public $forceEndpoint = '';        // overrides WSDL endpoint
7360
    public $proxyhost = '';
7361
    public $proxyport = '';
7362
    public $proxyusername = '';
7363
    public $proxypassword = '';
7364
    public $portName = '';                // port name to use in WSDL
7365
    public $xml_encoding = '';            // character set encoding of incoming (response) messages
7366
    public $http_encoding = false;
7367
    public $timeout = 0;                // HTTP connection timeout
7368
    public $response_timeout = 30;        // HTTP response timeout
7369
    public $endpointType = '';            // soap|wsdl, empty for WSDL initialization error
7370
    public $persistentConnection = false;
7371
    public $defaultRpcParams = false;    // This is no longer used
7372
    public $request = '';                // HTTP request
7373
    public $response = '';                // HTTP response
7374
    public $responseData = '';            // SOAP payload of response
7375
    public $cookies = [];            // Cookies from response or for request
7376
    public $decode_utf8 = true;        // toggles whether the parser decodes element content w/ utf8_decode()
7377
    public $operations = [];        // WSDL operations, empty for WSDL initialization error
7378
    public $curl_options = [];    // User-specified cURL options
7379
    public $bindingType = '';            // WSDL operation binding type
7380
    public $use_curl = false;            // whether to always try to use cURL
7381
7382
    /*
7383
     * fault related variables
7384
     */
7385
    /**
7386
     * @var      fault
0 ignored issues
show
Bug introduced by
The type fault was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7387
     * @access   public
7388
     */
7389
    public $fault;
7390
    /**
7391
     * @var      faultcode
0 ignored issues
show
Bug introduced by
The type faultcode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7392
     * @access   public
7393
     */
7394
    public $faultcode;
7395
    /**
7396
     * @var      faultstring
0 ignored issues
show
Bug introduced by
The type faultstring was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7397
     * @access   public
7398
     */
7399
    public $faultstring;
7400
    /**
7401
     * @var      faultdetail
0 ignored issues
show
Bug introduced by
The type faultdetail was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7402
     * @access   public
7403
     */
7404
    public $faultdetail;
7405
7406
    /**
7407
     * constructor
7408
     *
7409
     * @param    mixed   $endpoint         SOAP server or WSDL URL (string), or wsdl instance (object)
7410
     * @param    mixed   $wsdl             optional, set to 'wsdl' or true if using WSDL
7411
     * @param    string $proxyhost optional
7412
     * @param    string $proxyport optional
7413
     * @param    string $proxyusername optional
7414
     * @param    string $proxypassword optional
7415
     * @param    integer $timeout          set the connection timeout
7416
     * @param    integer $response_timeout set the response timeout
7417
     * @param    string  $portName         optional portName in WSDL document
7418
     * @access   public
7419
     */
7420
    public function __construct($endpoint, $wsdl = false, $proxyhost = false, $proxyport = false, $proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = '')
7421
    {
7422
        parent::__construct();
7423
        $this->endpoint = $endpoint;
7424
        $this->proxyhost = $proxyhost;
7425
        $this->proxyport = $proxyport;
7426
        $this->proxyusername = $proxyusername;
7427
        $this->proxypassword = $proxypassword;
7428
        $this->timeout = $timeout;
7429
        $this->response_timeout = $response_timeout;
7430
        $this->portName = $portName;
7431
7432
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7433
        $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7434
7435
        // make values
7436
        if ($wsdl) {
7437
            if (is_object($endpoint) && ('wsdl' == get_class($endpoint))) {
7438
                $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...
7439
                $this->endpoint = $this->wsdl->wsdl;
7440
                $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...
7441
                $this->debug('existing wsdl instance created from ' . $this->endpoint);
7442
                $this->checkWSDL();
7443
            } else {
7444
                $this->wsdlFile = $this->endpoint;
7445
                $this->wsdl = null;
7446
                $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
7447
            }
7448
            $this->endpointType = 'wsdl';
7449
        } else {
7450
            $this->debug("instantiate SOAP with endpoint at $endpoint");
7451
            $this->endpointType = 'soap';
7452
        }
7453
    }
7454
7455
    /**
7456
     * calls method, returns PHP native type
7457
     *
7458
     * @param    string $operation SOAP server URL or path
7459
     * @param    mixed $params An array, associative or simple, of the parameters
7460
     *                          for the method call, or a string that is the XML
7461
     *                          for the call.  For rpc style, this call will
7462
     *                          wrap the XML in a tag named after the method, as
7463
     *                          well as the SOAP Envelope and Body.  For document
7464
     *                          style, this will only wrap with the Envelope and Body.
7465
     *                          IMPORTANT: when using an array with document style,
7466
     *                          in which case there
7467
     *                         is really one parameter, the root of the fragment
7468
     *                         used in the call, which encloses what programmers
7469
     *                         normally think of parameters.  A parameter array
7470
     *                         *must* include the wrapper.
7471
     * @param    string $namespace optional method namespace (WSDL can override)
7472
     * @param    string $soapAction optional SOAPAction value (WSDL can override)
7473
     * @param    mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7474
     * @param    boolean $rpcParams optional (no longer used)
7475
     * @param    string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7476
     * @param    string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
7477
     * @return    mixed    response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
7478
     * @access   public
7479
     */
7480
    public function call($operation, $params = [], $namespace = 'http://tempuri.org', $soapAction = '', $headers = false, $rpcParams = null, $style = 'rpc', $use = 'encoded')
7481
    {
7482
        $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...
7483
        $this->fault = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type fault of property $fault.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
7484
        $this->setError('');
7485
        $this->request = '';
7486
        $this->response = '';
7487
        $this->responseData = '';
7488
        $this->faultstring = '';
0 ignored issues
show
Documentation Bug introduced by
It seems like '' of type string is incompatible with the declared type faultstring of property $faultstring.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
7489
        $this->faultcode = '';
0 ignored issues
show
Documentation Bug introduced by
It seems like '' of type string is incompatible with the declared type faultcode of property $faultcode.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
7490
        $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...
7491
7492
        $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7493
        $this->appendDebug('params=' . $this->varDump($params));
7494
        $this->appendDebug('headers=' . $this->varDump($headers));
7495
        if ($headers) {
7496
            $this->requestHeaders = $headers;
7497
        }
7498
        if ('wsdl' == $this->endpointType && null === $this->wsdl) {
7499
            $this->loadWSDL();
7500
            if ($this->getError()) {
7501
                return false;
7502
            }
7503
        }
7504
        // serialize parameters
7505
        if ('wsdl' == $this->endpointType && $opData = $this->getOperationData($operation)) {
7506
            // use WSDL for operation
7507
            $this->opData = $opData;
7508
            $this->debug('found operation');
7509
            $this->appendDebug('opData=' . $this->varDump($opData));
7510
            if (isset($opData['soapAction'])) {
7511
                $soapAction = $opData['soapAction'];
7512
            }
7513
            if (!$this->forceEndpoint) {
7514
                $this->endpoint = $opData['endpoint'];
7515
            } else {
7516
                $this->endpoint = $this->forceEndpoint;
7517
            }
7518
            $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7519
            $style = $opData['style'];
7520
            $use = $opData['input']['use'];
7521
            // add ns to ns array
7522
            if ('' != $namespace && !isset($this->wsdl->namespaces[$namespace])) {
7523
                $nsPrefix = 'ns' . mt_rand(1000, 9999);
7524
                $this->wsdl->namespaces[$nsPrefix] = $namespace;
7525
            }
7526
            $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7527
            // serialize payload
7528
            if (is_string($params)) {
7529
                $this->debug("serializing param string for WSDL operation $operation");
7530
                $payload = $params;
7531
            } elseif (is_array($params)) {
7532
                $this->debug("serializing param array for WSDL operation $operation");
7533
                $payload = $this->wsdl->serializeRPCParameters($operation, 'input', $params, $this->bindingType);
7534
            } else {
7535
                $this->debug('params must be array or string');
7536
                $this->setError('params must be array or string');
7537
                return false;
7538
            }
7539
            $usedNamespaces = $this->wsdl->usedNamespaces;
7540
            if (isset($opData['input']['encodingStyle'])) {
7541
                $encodingStyle = $opData['input']['encodingStyle'];
7542
            } else {
7543
                $encodingStyle = '';
7544
            }
7545
            $this->appendDebug($this->wsdl->getDebug());
7546
            $this->wsdl->clearDebug();
7547
            if ($errstr = $this->wsdl->getError()) {
7548
                $this->debug('got wsdl error: ' . $errstr);
7549
                $this->setError('wsdl error: ' . $errstr);
7550
                return false;
7551
            }
7552
        } elseif ('wsdl' == $this->endpointType) {
7553
            // operation not in WSDL
7554
            $this->appendDebug($this->wsdl->getDebug());
7555
            $this->wsdl->clearDebug();
7556
            $this->setError('operation ' . $operation . ' not present in WSDL.');
7557
            $this->debug("operation '$operation' not present in WSDL.");
7558
            return false;
7559
        } else {
7560
            // no WSDL
7561
            //$this->namespaces['ns1'] = $namespace;
7562
            $nsPrefix = 'ns' . mt_rand(1000, 9999);
7563
            // serialize
7564
            $payload = '';
7565
            if (is_string($params)) {
7566
                $this->debug("serializing param string for operation $operation");
7567
                $payload = $params;
7568
            } elseif (is_array($params)) {
7569
                $this->debug("serializing param array for operation $operation");
7570
                foreach ($params as $k => $v) {
7571
                    $payload .= $this->serialize_val($v, $k, false, false, false, false, $use);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type array expected by parameter $attributes of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

7571
                    $payload .= $this->serialize_val($v, $k, false, false, false, /** @scrutinizer ignore-type */ false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

7571
                    $payload .= $this->serialize_val($v, $k, false, false, /** @scrutinizer ignore-type */ false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $name_ns of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

7571
                    $payload .= $this->serialize_val($v, $k, false, /** @scrutinizer ignore-type */ false, false, false, $use);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $type of nusoap_base::serialize_val(). ( Ignorable by Annotation )

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

7571
                    $payload .= $this->serialize_val($v, $k, /** @scrutinizer ignore-type */ false, false, false, false, $use);
Loading history...
7572
                }
7573
            } else {
7574
                $this->debug('params must be array or string');
7575
                $this->setError('params must be array or string');
7576
                return false;
7577
            }
7578
            $usedNamespaces = [];
7579
            if ('encoded' == $use) {
7580
                $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7581
            } else {
7582
                $encodingStyle = '';
7583
            }
7584
        }
7585
        // wrap RPC calls with method element
7586
        if ('rpc' == $style) {
7587
            if ('literal' == $use) {
7588
                $this->debug('wrapping RPC request with literal method element');
7589
                if ($namespace) {
7590
                    // 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
7591
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7592
                        $payload .
7593
                        "</$nsPrefix:$operation>";
7594
                } else {
7595
                    $payload = "<$operation>" . $payload . "</$operation>";
7596
                }
7597
            } else {
7598
                $this->debug('wrapping RPC request with encoded method element');
7599
                if ($namespace) {
7600
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7601
                        $payload .
7602
                        "</$nsPrefix:$operation>";
7603
                } else {
7604
                    $payload = "<$operation>" .
7605
                        $payload .
7606
                        "</$operation>";
7607
                }
7608
            }
7609
        }
7610
        // serialize envelope
7611
        $soapmsg = $this->serializeEnvelope($payload, $this->requestHeaders, $usedNamespaces, $style, $use, $encodingStyle);
7612
        $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7613
        $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
7614
        // send
7615
        $return = $this->send($this->getHTTPBody($soapmsg), $soapAction, $this->timeout, $this->response_timeout);
7616
        if ($errstr = $this->getError()) {
7617
            $this->debug('Error: ' . $errstr);
7618
            return false;
7619
        } else {
7620
            $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...
7621
            $this->debug('sent message successfully and got a(n) ' . gettype($return));
7622
            $this->appendDebug('return=' . $this->varDump($return));
7623
7624
            // fault?
7625
            if (is_array($return) && isset($return['faultcode'])) {
7626
                $this->debug('got fault');
7627
                $this->setError($return['faultcode'] . ': ' . $return['faultstring']);
7628
                $this->fault = true;
7629
                foreach ($return as $k => $v) {
7630
                    $this->$k = $v;
7631
                    if (is_array($v)) {
7632
                        $this->debug("$k = " . json_encode($v));
7633
                    } else {
7634
                        $this->debug("$k = $v<br>");
7635
                    }
7636
                }
7637
                return $return;
7638
            } elseif ('document' == $style) {
7639
                // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7640
                // we are only going to return the first part here...sorry about that
7641
                return $return;
7642
            } else {
7643
                // array of return values
7644
                if (is_array($return)) {
7645
                    // multiple 'out' parameters, which we return wrapped up
7646
                    // in the array
7647
                    if (count($return) > 1) {
7648
                        return $return;
7649
                    }
7650
                    // single 'out' parameter (normally the return value)
7651
                    $return = array_shift($return);
7652
                    $this->debug('return shifted value: ');
7653
                    $this->appendDebug($this->varDump($return));
7654
                    return $return;
7655
                // nothing returned (ie, echoVoid)
7656
                } else {
7657
                    return '';
7658
                }
7659
            }
7660
        }
7661
    }
7662
7663
    /**
7664
     * check WSDL passed as an instance or pulled from an endpoint
7665
     *
7666
     * @access   private
7667
     */
7668
    private function checkWSDL()
7669
    {
7670
        $this->appendDebug($this->wsdl->getDebug());
7671
        $this->wsdl->clearDebug();
7672
        $this->debug('checkWSDL');
7673
        // catch errors
7674
        if ($errstr = $this->wsdl->getError()) {
7675
            $this->appendDebug($this->wsdl->getDebug());
7676
            $this->wsdl->clearDebug();
7677
            $this->debug('got wsdl error: ' . $errstr);
7678
            $this->setError('wsdl error: ' . $errstr);
7679
        } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) {
7680
            $this->appendDebug($this->wsdl->getDebug());
7681
            $this->wsdl->clearDebug();
7682
            $this->bindingType = 'soap';
7683
            $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7684
        } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
7685
            $this->appendDebug($this->wsdl->getDebug());
7686
            $this->wsdl->clearDebug();
7687
            $this->bindingType = 'soap12';
7688
            $this->debug('got ' . count($this->operations) . ' operations from wsdl ' . $this->wsdlFile . ' for binding type ' . $this->bindingType);
7689
            $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7690
        } else {
7691
            $this->appendDebug($this->wsdl->getDebug());
7692
            $this->wsdl->clearDebug();
7693
            $this->debug('getOperations returned false');
7694
            $this->setError('no operations defined in the WSDL document!');
7695
        }
7696
    }
7697
7698
    /**
7699
     * instantiate wsdl object and parse wsdl file
7700
     *
7701
     * @access    public
7702
     */
7703
    public function loadWSDL()
7704
    {
7705
        $this->debug('instantiating wsdl class with doc: ' . $this->wsdlFile);
7706
        $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...
7707
        $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7708
        $this->wsdl->fetchWSDL($this->wsdlFile);
7709
        $this->checkWSDL();
7710
    }
7711
7712
    /**
7713
     * get available data pertaining to an operation
7714
     *
7715
     * @param    string $operation operation name
7716
     * @return    array array of data pertaining to the operation
7717
     * @access   public
7718
     */
7719
    public function getOperationData($operation)
7720
    {
7721
        if ('wsdl' == $this->endpointType && null === $this->wsdl) {
7722
            $this->loadWSDL();
7723
            if ($this->getError()) {
7724
                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 array.
Loading history...
7725
            }
7726
        }
7727
        if (isset($this->operations[$operation])) {
7728
            return $this->operations[$operation];
7729
        }
7730
        $this->debug("No data for operation: $operation");
7731
    }
7732
7733
    /**
7734
     * send the SOAP message
7735
     *
7736
     * Note: if the operation has multiple return values
7737
     * the return value of this method will be an array
7738
     * of those values.
7739
     *
7740
     * @param    string $msg a SOAPx4 soapmsg object
7741
     * @param    string $soapaction SOAPAction value
7742
     * @param    integer $timeout set connection timeout in seconds
7743
     * @param    integer $response_timeout set response timeout in seconds
7744
     * @return    mixed native PHP types.
7745
     * @access   private
7746
     */
7747
    private function send($msg, $soapaction = '', $timeout = 0, $response_timeout = 30)
7748
    {
7749
        $this->checkCookies();
7750
        // detect transport
7751
        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...
7752
            // http(s)
7753
            case preg_match('/^http/', $this->endpoint):
7754
                $this->debug('transporting via HTTP');
7755
                if (true == $this->persistentConnection && is_object($this->persistentConnection)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
introduced by
The condition is_object($this->persistentConnection) is always false.
Loading history...
7756
                    $http =& $this->persistentConnection;
7757
                } else {
7758
                    $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7759
                    if ($this->persistentConnection) {
7760
                        $http->usePersistentConnection();
7761
                    }
7762
                }
7763
                $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7764
                $http->setSOAPAction($soapaction);
7765
                if ($this->proxyhost && $this->proxyport) {
7766
                    $http->setProxy($this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
0 ignored issues
show
Bug introduced by
It seems like $this->proxyhost can also be of type true; however, parameter $proxyhost of soap_transport_http::setProxy() 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

7766
                    $http->setProxy(/** @scrutinizer ignore-type */ $this->proxyhost, $this->proxyport, $this->proxyusername, $this->proxypassword);
Loading history...
Bug introduced by
It seems like $this->proxyport can also be of type true; however, parameter $proxyport of soap_transport_http::setProxy() 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

7766
                    $http->setProxy($this->proxyhost, /** @scrutinizer ignore-type */ $this->proxyport, $this->proxyusername, $this->proxypassword);
Loading history...
7767
                }
7768
                if ('' != $this->authtype) {
7769
                    $http->setCredentials($this->username, $this->password, $this->authtype, [], $this->certRequest);
7770
                }
7771
                if ('' != $this->http_encoding) {
7772
                    $http->setEncoding($this->http_encoding);
7773
                }
7774
                $this->debug('sending message, length=' . strlen($msg));
7775
                if (preg_match('/^http:/', $this->endpoint)) {
7776
                    //if(strpos($this->endpoint,'http:')){
7777
                    $this->responseData = $http->send($msg, $timeout, $response_timeout, $this->cookies);
7778
                } elseif (preg_match('/^https/', $this->endpoint)) {
7779
                    //} elseif(strpos($this->endpoint,'https:')){
7780
                    //if(phpversion() == '4.3.0-dev'){
7781
                    //$response = $http->send($msg,$timeout,$response_timeout);
7782
                    //$this->request = $http->outgoing_payload;
7783
                    //$this->response = $http->incoming_payload;
7784
                    //} else
7785
                    $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

7785
                    $this->responseData = /** @scrutinizer ignore-deprecated */ $http->sendHTTPS($msg, $timeout, $response_timeout, $this->cookies);
Loading history...
7786
                } else {
7787
                    $this->setError('no http/s in endpoint url');
7788
                }
7789
                $this->request = $http->outgoing_payload;
7790
                $this->response = $http->incoming_payload;
7791
                $this->appendDebug($http->getDebug());
7792
                $this->UpdateCookies($http->incoming_cookies);
7793
7794
                // save transport object if using persistent connections
7795
                if ($this->persistentConnection) {
7796
                    $http->clearDebug();
7797
                    if (!is_object($this->persistentConnection)) {
0 ignored issues
show
introduced by
The condition is_object($this->persistentConnection) is always false.
Loading history...
7798
                        $this->persistentConnection = $http;
7799
                    }
7800
                }
7801
7802
                if ($err = $http->getError()) {
7803
                    $this->setError('HTTP Error: ' . $err);
7804
                    return false;
7805
                } elseif ($this->getError()) {
7806
                    return false;
7807
                } else {
7808
                    $this->debug('got response, length=' . strlen($this->responseData) . ' type=' . $http->incoming_headers['content-type']);
7809
                    return $this->parseResponse($http->incoming_headers, $this->responseData);
7810
                }
7811
                break;
7812
            default:
7813
                $this->setError('no transport found, or selected transport is not yet supported!');
7814
                return false;
7815
                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...
7816
        }
7817
    }
7818
7819
    /**
7820
     * processes SOAP message returned from server
7821
     *
7822
     * @param    array $headers The HTTP headers
7823
     * @param    string $data unprocessed response data from server
7824
     * @return    mixed    value of the message, decoded into a PHP type
7825
     * @access   private
7826
     */
7827
    private function parseResponse($headers, $data)
7828
    {
7829
        $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
7830
        $this->appendDebug($this->varDump($headers));
7831
        if (!isset($headers['content-type'])) {
7832
            $this->setError('Response not of type text/xml (no content-type header)');
7833
            return false;
7834
        }
7835
        if (false === strpos($headers['content-type'], 'text/xml')) {
7836
            $this->setError('Response not of type text/xml: ' . $headers['content-type']);
7837
            return false;
7838
        }
7839
        if (strpos($headers['content-type'], '=')) {
7840
            $enc = str_replace('"', '', substr(strstr($headers['content-type'], '='), 1));
7841
            $this->debug('Got response encoding: ' . $enc);
7842
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
7843
                $this->xml_encoding = strtoupper($enc);
7844
            } else {
7845
                $this->xml_encoding = 'US-ASCII';
7846
            }
7847
        } else {
7848
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
7849
            $this->xml_encoding = 'ISO-8859-1';
7850
        }
7851
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
7852
        $parser = new nusoap_parser($data, $this->xml_encoding, $this->operations, $this->decode_utf8);
0 ignored issues
show
Bug introduced by
$this->operations of type array is incompatible with the type string expected by parameter $method of nusoap_parser::__construct(). ( Ignorable by Annotation )

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

7852
        $parser = new nusoap_parser($data, $this->xml_encoding, /** @scrutinizer ignore-type */ $this->operations, $this->decode_utf8);
Loading history...
7853
        // add parser debug data to our debug
7854
        $this->appendDebug($parser->getDebug());
7855
        // if parse errors
7856
        if ($errstr = $parser->getError()) {
7857
            $this->setError($errstr);
7858
            // destroy the parser object
7859
            unset($parser);
7860
            return false;
7861
        } else {
7862
            // get SOAP headers
7863
            $this->responseHeaders = $parser->getHeaders();
7864
            // get SOAP headers
7865
            $this->responseHeader = $parser->get_soapheader();
7866
            // get decoded message
7867
            $return = $parser->get_soapbody();
7868
            // add document for doclit support
7869
            $this->document = $parser->document;
7870
            // destroy the parser object
7871
            unset($parser);
7872
            // return decode message
7873
            return $return;
7874
        }
7875
    }
7876
7877
    /**
7878
     * sets user-specified cURL options
7879
     *
7880
     * @param    mixed $option The cURL option (always integer?)
7881
     * @param    mixed $value The cURL option value
7882
     * @access   public
7883
     */
7884
    public function setCurlOption($option, $value)
7885
    {
7886
        $this->debug("setCurlOption option=$option, value=");
7887
        $this->appendDebug($this->varDump($value));
7888
        $this->curl_options[$option] = $value;
7889
    }
7890
7891
    /**
7892
     * sets the SOAP endpoint, which can override WSDL
7893
     *
7894
     * @param    string $endpoint The endpoint URL to use, or empty string or false to prevent override
7895
     * @access   public
7896
     */
7897
    public function setEndpoint($endpoint)
7898
    {
7899
        $this->debug("setEndpoint(\"$endpoint\")");
7900
        $this->forceEndpoint = $endpoint;
7901
    }
7902
7903
    /**
7904
     * set the SOAP headers
7905
     *
7906
     * @param    mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
7907
     * @access   public
7908
     */
7909
    public function setHeaders($headers)
7910
    {
7911
        $this->debug('setHeaders headers=');
7912
        $this->appendDebug($this->varDump($headers));
7913
        $this->requestHeaders = $headers;
7914
    }
7915
7916
    /**
7917
     * get the SOAP response headers (namespace resolution incomplete)
7918
     *
7919
     * @return    string
7920
     * @access   public
7921
     */
7922
    public function getHeaders()
7923
    {
7924
        return $this->responseHeaders;
7925
    }
7926
7927
    /**
7928
     * get the SOAP response Header (parsed)
7929
     *
7930
     * @return    mixed
7931
     * @access   public
7932
     */
7933
    public function getHeader()
7934
    {
7935
        return $this->responseHeader;
7936
    }
7937
7938
    /**
7939
     * set proxy info here
7940
     *
7941
     * @param    string $proxyhost
7942
     * @param    string $proxyport
7943
     * @param    string $proxyusername
7944
     * @param    string $proxypassword
7945
     * @access   public
7946
     */
7947
    public function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
7948
    {
7949
        $this->proxyhost = $proxyhost;
7950
        $this->proxyport = $proxyport;
7951
        $this->proxyusername = $proxyusername;
7952
        $this->proxypassword = $proxypassword;
7953
    }
7954
7955
    /**
7956
     * if authenticating, set user credentials here
7957
     *
7958
     * @param    string $username
7959
     * @param    string $password
7960
     * @param    string $authtype (basic|digest|certificate|ntlm)
7961
     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
7962
     * @access   public
7963
     */
7964
    public function setCredentials($username, $password, $authtype = 'basic', $certRequest = [])
7965
    {
7966
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
7967
        $this->appendDebug($this->varDump($certRequest));
7968
        $this->username = $username;
7969
        $this->password = $password;
7970
        $this->authtype = $authtype;
7971
        $this->certRequest = $certRequest;
7972
    }
7973
7974
    /**
7975
     * use HTTP encoding
7976
     *
7977
     * @param    string $enc HTTP encoding
7978
     * @access   public
7979
     */
7980
    public function setHTTPEncoding($enc = 'gzip, deflate')
7981
    {
7982
        $this->debug("setHTTPEncoding(\"$enc\")");
7983
        $this->http_encoding = $enc;
7984
    }
7985
7986
    /**
7987
     * Set whether to try to use cURL connections if possible
7988
     *
7989
     * @param    boolean $use Whether to try to use cURL
7990
     * @access   public
7991
     */
7992
    public function setUseCURL($use)
7993
    {
7994
        $this->debug("setUseCURL($use)");
7995
        $this->use_curl = $use;
7996
    }
7997
7998
    /**
7999
     * use HTTP persistent connections if possible
8000
     *
8001
     * @access   public
8002
     */
8003
    public function useHTTPPersistentConnection()
8004
    {
8005
        $this->debug('useHTTPPersistentConnection');
8006
        $this->persistentConnection = true;
8007
    }
8008
8009
    /**
8010
     * gets the default RPC parameter setting.
8011
     * If true, default is that call params are like RPC even for document style.
8012
     * Each call() can override this value.
8013
     *
8014
     * This is no longer used.
8015
     *
8016
     * @return boolean
8017
     * @access public
8018
     * @deprecated
8019
     */
8020
    public function getDefaultRpcParams()
8021
    {
8022
        return $this->defaultRpcParams;
8023
    }
8024
8025
    /**
8026
     * sets the default RPC parameter setting.
8027
     * If true, default is that call params are like RPC even for document style
8028
     * Each call() can override this value.
8029
     *
8030
     * This is no longer used.
8031
     *
8032
     * @param    boolean $rpcParams
8033
     * @access public
8034
     * @deprecated
8035
     */
8036
    public function setDefaultRpcParams($rpcParams)
8037
    {
8038
        $this->defaultRpcParams = $rpcParams;
8039
    }
8040
8041
    /**
8042
     * dynamically creates an instance of a proxy class,
8043
     * allowing user to directly call methods from wsdl
8044
     *
8045
     * @return   object soap_proxy object
8046
     * @access   public
8047
     */
8048
    public function getProxy()
8049
    {
8050
        $r = mt_rand();
8051
        $evalStr = $this->_getProxyClassCode($r);
8052
        //$this->debug("proxy class: $evalStr");
8053
        if ($this->getError()) {
8054
            $this->debug('Error from _getProxyClassCode, so return null');
8055
            return null;
8056
        }
8057
        // eval the class
8058
        eval($evalStr);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
8059
        // instantiate proxy object
8060
        eval("\$proxy = new nusoap_proxy_$r('');");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
8061
        // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
8062
        $proxy->endpointType = 'wsdl';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $proxy seems to be never defined.
Loading history...
8063
        $proxy->wsdlFile = $this->wsdlFile;
8064
        $proxy->wsdl = $this->wsdl;
8065
        $proxy->operations = $this->operations;
8066
        $proxy->defaultRpcParams = $this->defaultRpcParams;
8067
        // transfer other state
8068
        $proxy->soap_defencoding = $this->soap_defencoding;
8069
        $proxy->username = $this->username;
8070
        $proxy->password = $this->password;
8071
        $proxy->authtype = $this->authtype;
8072
        $proxy->certRequest = $this->certRequest;
8073
        $proxy->requestHeaders = $this->requestHeaders;
8074
        $proxy->endpoint = $this->endpoint;
8075
        $proxy->forceEndpoint = $this->forceEndpoint;
8076
        $proxy->proxyhost = $this->proxyhost;
8077
        $proxy->proxyport = $this->proxyport;
8078
        $proxy->proxyusername = $this->proxyusername;
8079
        $proxy->proxypassword = $this->proxypassword;
8080
        $proxy->http_encoding = $this->http_encoding;
8081
        $proxy->timeout = $this->timeout;
8082
        $proxy->response_timeout = $this->response_timeout;
8083
        $proxy->persistentConnection = &$this->persistentConnection;
8084
        $proxy->decode_utf8 = $this->decode_utf8;
8085
        $proxy->curl_options = $this->curl_options;
8086
        $proxy->bindingType = $this->bindingType;
8087
        $proxy->use_curl = $this->use_curl;
8088
        return $proxy;
8089
    }
8090
8091
    /**
8092
     * dynamically creates proxy class code
8093
     *
8094
     * @return   string PHP/NuSOAP code for the proxy class
8095
     * @access   private
8096
     */
8097
    private function _getProxyClassCode($r)
8098
    {
8099
        $this->debug("in getProxy endpointType=$this->endpointType");
8100
        $this->appendDebug('wsdl=' . $this->varDump($this->wsdl));
8101
        if ('wsdl' != $this->endpointType) {
8102
            $evalStr = 'A proxy can only be created for a WSDL client';
8103
            $this->setError($evalStr);
8104
            $evalStr = "echo \"$evalStr\";";
8105
            return $evalStr;
8106
        }
8107
        if ('wsdl' == $this->endpointType && null === $this->wsdl) {
8108
            $this->loadWSDL();
8109
            if ($this->getError()) {
8110
                return 'echo "' . $this->getError() . '";';
8111
            }
8112
        }
8113
        $evalStr = '';
8114
        foreach ($this->operations as $operation => $opData) {
8115
            if ('' != $operation) {
8116
                // create param string and param comment string
8117
                if (count($opData['input']['parts']) > 0) {
8118
                    $paramStr = '';
8119
                    $paramArrayStr = '';
8120
                    $paramCommentStr = '';
8121
                    foreach ($opData['input']['parts'] as $name => $type) {
8122
                        $paramStr .= "\$$name, ";
8123
                        $paramArrayStr .= "'$name' => \$$name, ";
8124
                        $paramCommentStr .= "$type \$$name, ";
8125
                    }
8126
                    $paramStr = substr($paramStr, 0, -2);
8127
                    $paramArrayStr = substr($paramArrayStr, 0, -2);
8128
                    $paramCommentStr = substr($paramCommentStr, 0, -2);
8129
                } else {
8130
                    $paramStr = '';
8131
                    $paramArrayStr = '';
8132
                    $paramCommentStr = 'void';
8133
                }
8134
                $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
8135
                $evalStr .= "// $paramCommentStr
8136
	function " . str_replace('.', '__', $operation) . "($paramStr) {
8137
		\$params = array($paramArrayStr);
8138
		return \$this->call('$operation', \$params, '" . $opData['namespace'] . "', '" . (isset($opData['soapAction']) ? $opData['soapAction'] : '') . "');
8139
	}
8140
	";
8141
                unset($paramStr);
8142
                unset($paramCommentStr);
8143
            }
8144
        }
8145
        $evalStr = 'class nusoap_proxy_' . $r . ' extends nusoap_client {
8146
	' . $evalStr . '
8147
}';
8148
        return $evalStr;
8149
    }
8150
8151
    /**
8152
     * dynamically creates proxy class code
8153
     *
8154
     * @return   string PHP/NuSOAP code for the proxy class
8155
     * @access   public
8156
     */
8157
    public function getProxyClassCode()
8158
    {
8159
        $r = mt_rand();
8160
        return $this->_getProxyClassCode($r);
8161
    }
8162
8163
    /**
8164
     * gets the HTTP body for the current request.
8165
     *
8166
     * @param string $soapmsg The SOAP payload
8167
     * @return string The HTTP body, which includes the SOAP payload
8168
     * @access private
8169
     */
8170
    private function getHTTPBody($soapmsg)
8171
    {
8172
        return $soapmsg;
8173
    }
8174
8175
    /**
8176
     * gets the HTTP content type for the current request.
8177
     *
8178
     * Note: getHTTPBody must be called before this.
8179
     *
8180
     * @return string the HTTP content type for the current request.
8181
     * @access private
8182
     */
8183
    private function getHTTPContentType()
8184
    {
8185
        return 'text/xml';
8186
    }
8187
8188
    /**
8189
     * gets the HTTP content type charset for the current request.
8190
     * returns false for non-text content types.
8191
     *
8192
     * Note: getHTTPBody must be called before this.
8193
     *
8194
     * @return string the HTTP content type charset for the current request.
8195
     * @access private
8196
     */
8197
    private function getHTTPContentTypeCharset()
8198
    {
8199
        return $this->soap_defencoding;
8200
    }
8201
8202
    /*
8203
    * whether or not parser should decode utf8 element content
8204
    *
8205
    * @return   always returns true
0 ignored issues
show
Bug introduced by
The type always was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8206
    * @access   public
8207
    */
8208
    public function decodeUTF8($bool)
8209
    {
8210
        $this->decode_utf8 = $bool;
8211
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type always.
Loading history...
8212
    }
8213
8214
    /**
8215
     * adds a new Cookie into $this->cookies array
8216
     *
8217
     * @param    string $name Cookie Name
8218
     * @param    string $value Cookie Value
8219
     * @return    boolean if cookie-set was successful returns true, else false
8220
     * @access    public
8221
     */
8222
    public function setCookie($name, $value)
8223
    {
8224
        if (0 == strlen($name)) {
8225
            return false;
8226
        }
8227
        $this->cookies[] = ['name' => $name, 'value' => $value];
8228
        return true;
8229
    }
8230
8231
    /**
8232
     * gets all Cookies
8233
     *
8234
     * @return   array with all internal cookies
8235
     * @access   public
8236
     */
8237
    public function getCookies()
8238
    {
8239
        return $this->cookies;
8240
    }
8241
8242
    /**
8243
     * checks all Cookies and delete those which are expired
8244
     *
8245
     * @return   boolean always return true
8246
     * @access   private
8247
     */
8248
    private function checkCookies()
8249
    {
8250
        if (0 == count($this->cookies)) {
8251
            return true;
8252
        }
8253
        $this->debug('checkCookie: check ' . count($this->cookies) . ' cookies');
8254
        $curr_cookies = $this->cookies;
8255
        $this->cookies = [];
8256
        foreach ($curr_cookies as $cookie) {
8257
            if (!is_array($cookie)) {
8258
                $this->debug('Remove cookie that is not an array');
8259
                continue;
8260
            }
8261
            if ((isset($cookie['expires'])) && (!empty($cookie['expires']))) {
8262
                if (strtotime($cookie['expires']) > time()) {
8263
                    $this->cookies[] = $cookie;
8264
                } else {
8265
                    $this->debug('Remove expired cookie ' . $cookie['name']);
8266
                }
8267
            } else {
8268
                $this->cookies[] = $cookie;
8269
            }
8270
        }
8271
        $this->debug('checkCookie: ' . count($this->cookies) . ' cookies left in array');
8272
        return true;
8273
    }
8274
8275
    /**
8276
     * updates the current cookies with a new set
8277
     *
8278
     * @param    array $cookies new cookies with which to update current ones
8279
     * @return    boolean always return true
8280
     * @access    private
8281
     */
8282
    private function UpdateCookies($cookies)
8283
    {
8284
        if (0 == count($this->cookies)) {
8285
            // no existing cookies: take whatever is new
8286
            if (count($cookies) > 0) {
8287
                $this->debug('Setting new cookie(s)');
8288
                $this->cookies = $cookies;
8289
            }
8290
            return true;
8291
        }
8292
        if (0 == count($cookies)) {
8293
            // no new cookies: keep what we've got
8294
            return true;
8295
        }
8296
        // merge
8297
        foreach ($cookies as $newCookie) {
8298
            if (!is_array($newCookie)) {
8299
                continue;
8300
            }
8301
            if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
8302
                continue;
8303
            }
8304
            $newName = $newCookie['name'];
8305
8306
            $found = false;
8307
            for ($i = 0, $iMax = count($this->cookies); $i < $iMax; $i++) {
8308
                $cookie = $this->cookies[$i];
8309
                if (!is_array($cookie)) {
8310
                    continue;
8311
                }
8312
                if (!isset($cookie['name'])) {
8313
                    continue;
8314
                }
8315
                if ($newName != $cookie['name']) {
8316
                    continue;
8317
                }
8318
                $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
8319
                $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
8320
                if ($newDomain != $domain) {
8321
                    continue;
8322
                }
8323
                $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
8324
                $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
8325
                if ($newPath != $path) {
8326
                    continue;
8327
                }
8328
                $this->cookies[$i] = $newCookie;
8329
                $found = true;
8330
                $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
8331
                break;
8332
            }
8333
            if (!$found) {
8334
                $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
8335
                $this->cookies[] = $newCookie;
8336
            }
8337
        }
8338
        return true;
8339
    }
8340
}
8341
8342
8343
if (!extension_loaded('soap')) {
8344
    /**
8345
     *    For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
8346
     */
8347
    class soapclient extends nusoap_client
8348
    {
8349
    }
8350
}
8351