Passed
Push — master ( 792a3a...182ddb )
by Gaetano
09:37
created

Client::sendPayloadHTTPS()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 20
dl 0
loc 10
ccs 0
cts 0
cp 0
crap 2
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace PhpXmlRpc;
4
5
//use PhpXmlRpc\Helper\Charset;
6
use PhpXmlRpc\Exception\ValueErrorException;
7
use PhpXmlRpc\Helper\XMLParser;
8
use PhpXmlRpc\Traits\DeprecationLogger;
9
10
/**
11
 * Used to represent a client of an XML-RPC server.
12
 */
13
class Client
14
{
15
    use DeprecationLogger;
16
17
    const USE_CURL_NEVER = 0;
18
    const USE_CURL_ALWAYS = 1;
19
    const USE_CURL_AUTO = 2;
20
21
    const OPT_ACCEPTED_CHARSET_ENCODINGS = 'accepted_charset_encodings';
22
    const OPT_ACCEPTED_COMPRESSION = 'accepted_compression';
23
    const OPT_AUTH_TYPE = 'authtype';
24
    const OPT_CA_CERT = 'cacert';
25
    const OPT_CA_CERT_DIR = 'cacertdir';
26
    const OPT_CERT = 'cert';
27
    const OPT_CERT_PASS = 'certpass';
28
    const OPT_COOKIES = 'cookies';
29
    const OPT_DEBUG = 'debug';
30
    const OPT_EXTRA_CURL_OPTS = 'extracurlopts';
31
    const OPT_KEEPALIVE = 'keepalive';
32
    const OPT_KEY = 'key';
33
    const OPT_KEY_PASS = 'keypass';
34
    const OPT_NO_MULTICALL = 'no_multicall';
35
    const OPT_PASSWORD = 'password';
36
    const OPT_PROXY = 'proxy';
37
    const OPT_PROXY_AUTH_TYPE = 'proxy_authtype';
38
    const OPT_PROXY_PASS = 'proxy_pass';
39
    const OPT_PROXY_PORT = 'proxyport';
40
    const OPT_PROXY_USER = 'proxy_user';
41
    const OPT_REQUEST_CHARSET_ENCODING = 'request_charset_encoding';
42
    const OPT_REQUEST_COMPRESSION = 'request_compression';
43
    const OPT_RETURN_TYPE = 'return_type';
44
    const OPT_SSL_VERSION = 'sslversion';
45
    const OPT_TIMEOUT = 'timeout';
46
    const OPT_USERNAME = 'username';
47
    const OPT_USER_AGENT = 'user_agent';
48
    const OPT_USE_CURL = 'use_curl';
49
    const OPT_VERIFY_HOST = 'verifyhost';
50
    const OPT_VERIFY_PEER = 'verifypeer';
51
52
    /** @var string */
53
    protected static $requestClass = '\\PhpXmlRpc\\Request';
54
    /** @var string */
55
    protected static $responseClass = '\\PhpXmlRpc\\Response';
56
57
    /**
58
     * @var int
59
     * @deprecated will be removed in the future
60
     */
61
    public $errno;
62
    /**
63
     * @var string
64
     * @deprecated will be removed in the future
65
     */
66
    public $errstr;
67
68
    /// @todo: do all the ones below need to be public?
69
70
    /**
71
     * @var string
72
     * @internal use getUrl/__construct
73
     */
74
    public $method = 'http';
75
    /**
76
     * @var string
77
     * @internal use getUrl/__construct
78
     */
79
    public $server;
80
    /**
81
     * @var int
82
     * @internal use getUrl/__construct
83
     */
84
    public $port = 0;
85
    /**
86
     * @var string
87
     * @internal use getUrl/__construct
88
     */
89
    public $path;
90
91
    /**
92
     * @var int
93
     * @internal use setOption/getOption
94
     */
95
    public $debug = 0;
96
    /**
97
     * @var string
98
     * @internal use setCredentials/getOption
99
     */
100
    public $username = '';
101
    /**
102
     * @var string
103
     * @internal use setCredentials/getOption
104
     */
105
    public $password = '';
106
    /**
107
     * @var int
108
     * @internal use setCredentials/getOption
109
     */
110
    public $authtype = 1;
111
    /**
112
     * @var string
113
     * @internal use setCertificate/getOption
114
     */
115
    public $cert = '';
116
    /**
117
     * @var string
118
     * @internal use setCertificate/getOption
119
     */
120
    public $certpass = '';
121
    /**
122 2
     * @var string
123
     * @internal use setCaCertificate/getOption
124 2
     */
125 2
    public $cacert = '';
126
    /**
127 2
     * @var string
128
     * @internal use setCaCertificate/getOption
129
     */
130
    public $cacertdir = '';
131
    /**
132
     * @var string
133
     * @internal use setKey/getOption
134
     */
135
    public $key = '';
136
    /**
137
     * @var string
138
     * @internal use setKey/getOption
139
     */
140
    public $keypass = '';
141
    /**
142
     * @var bool
143
     * @internal use setOption/getOption
144
     */
145
    public $verifypeer = true;
146
    /**
147
     * @var int
148
     * @internal use setOption/getOption
149
     */
150
    public $verifyhost = 2;
151
    /**
152 718
     * @var int
153
     * @internal use setOption/getOption
154
     */
155 718
    public $sslversion = 0; // corresponds to CURL_SSLVERSION_DEFAULT
156 7
    /**
157 7
     * @var string
158 7
     * @internal use setProxy/getOption
159 7
     */
160
    public $proxy = '';
161
    /**
162 7
     * @var int
163
     * @internal use setProxy/getOption
164
     */
165 7
    public $proxyport = 0;
166
    /**
167
     * @var string
168 7
     * @internal use setProxy/getOption
169 7
     */
170
    public $proxy_user = '';
171 7
    /**
172
     * @var string
173
     * @internal use setProxy/getOption
174 7
     */
175
    public $proxy_pass = '';
176
    /**
177
     * @var int
178 718
     * @internal use setProxy/getOption
179
     */
180
    public $proxy_authtype = 1;
181 718
    /**
182
     * @var array
183 718
     * @internal use setCookie/getOption
184 718
     */
185 3
    public $cookies = array();
186
    /**
187 718
     * @var array
188 7
     * @internal use setOption/getOption
189
     */
190
    public $extracurlopts = array();
191
    /**
192 718
     * @var int
193
     * @internal use setOption/getOption
194 711
     */
195
    public $timeout = 0;
196
    /**
197 718
     * @var int
198
     * @internal use setOption/getOption
199
     */
200
    public $use_curl = self::USE_CURL_AUTO;
201 718
    /**
202
     * @var bool
203
     *
204 718
     * This determines whether the multicall() method will try to take advantage of the system.multicall xml-rpc method
205
     * to dispatch to the server an array of requests in a single http roundtrip or simply execute many consecutive http
206
     * calls. Defaults to FALSE, but it will be enabled automatically on the first failure of execution of
207
     * system.multicall.
208
     *
209
     * @internal use setOption/getOption
210
     */
211
    public $no_multicall = false;
212
    /**
213
     * @var array
214
     *
215
     * List of http compression methods accepted by the client for responses.
216
     * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib.
217 718
     *
218 718
     * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since in those cases it will be up to CURL to
219
     * decide the compression methods it supports. You might check for the presence of 'zlib' in the output of
220
     * curl_version() to determine whether compression is supported or not
221
     *
222
     * @internal use setAcceptedCompression/getOption
223
     */
224
    public $accepted_compression = array();
225
    /**
226
     * @var string|null
227
     *
228
     * Name of compression scheme to be used for sending requests.
229
     * Either null, 'gzip' or 'deflate'.
230
     *
231
     * @internal use setOption/getOption
232
     */
233 714
    public $request_compression = '';
234
    /**
235 714
     * @var bool
236 714
     *
237
     * Whether to use persistent connections for http 1.1 and https. Value set at constructor time.
238
     *
239
     * @internal use setOption/getOption
240
     */
241
    public $keepalive = false;
242
    /**
243
     * @var string[]
244
     *
245
     * Charset encodings that can be decoded without problems by the client. Value set at constructor time
246
     *
247
     * @internal use setOption/getOption
248
     */
249
    public $accepted_charset_encodings = array();
250
    /**
251
     * @var string
252 66
     *
253
     * The charset encoding that will be used for serializing request sent by the client.
254 66
     * It defaults to NULL, which means using US-ASCII and encoding all characters outside the ASCII printable range
255 66
     * using their xml character entity representation (this has the benefit that line end characters will not be mangled
256 66
     * in the transfer, a CR-LF will be preserved as well as a singe LF).
257 66
     * Valid values are 'US-ASCII', 'UTF-8' and 'ISO-8859-1'.
258
     * For the fastest mode of operation, set your both your app internal encoding and this to UTF-8.
259
     *
260
     * @internal use setOption/getOption
261
     */
262
    public $request_charset_encoding = '';
263
    /**
264
     * @var string
265
     *
266
     * Decides the content of Response objects returned by calls to send() and multicall().
267
     * Valid values are 'xmlrpcvals', 'phpvals' or 'xml'.
268
     *
269
     * Determines whether the value returned inside a Response object as results of calls to the send() and multicall()
270
     * methods will be a Value object, a plain php value or a raw xml string.
271
     * Allowed values are 'xmlrpcvals' (the default), 'phpvals' and 'xml'.
272
     * To allow the user to differentiate between a correct and a faulty response, fault responses will be returned as
273
     * Response objects in any case.
274
     * Note that the 'phpvals' setting will yield faster execution times, but some of the information from the original
275
     * response will be lost. It will be e.g. impossible to tell whether a particular php string value was sent by the
276
     * server as an xml-rpc string or base64 value.
277
     *
278
     * @internal use setOption/getOption
279
     */
280
    public $return_type = XMLParser::RETURN_XMLRPCVALS;
281
    /**
282
     * @var string
283
     *
284
     * Sent to servers in http headers. Value set at constructor time.
285
     *
286
     * @internal use setOption/getOption
287
     */
288
    public $user_agent;
289
290
    /**
291
     * CURL handle: used for keep-alive
292
     * @internal
293
     */
294
    public $xmlrpc_curl_handle = null;
295
296
    /**
297
     * @var array
298
     */
299
    protected $options = array(
300
        self::OPT_ACCEPTED_CHARSET_ENCODINGS,
301
        self::OPT_ACCEPTED_COMPRESSION,
302
        self::OPT_AUTH_TYPE,
303
        self::OPT_CA_CERT,
304
        self::OPT_CA_CERT_DIR,
305
        self::OPT_CERT,
306
        self::OPT_CERT_PASS,
307
        self::OPT_COOKIES,
308
        self::OPT_DEBUG,
309
        self::OPT_EXTRA_CURL_OPTS,
310
        self::OPT_KEEPALIVE,
311
        self::OPT_KEY,
312
        self::OPT_KEY_PASS,
313
        self::OPT_NO_MULTICALL,
314
        self::OPT_PASSWORD,
315
        self::OPT_PROXY,
316 132
        self::OPT_PROXY_AUTH_TYPE,
317
        self::OPT_PROXY_PASS,
318 132
        self::OPT_PROXY_USER,
319 132
        self::OPT_PROXY_PORT,
320
        self::OPT_REQUEST_CHARSET_ENCODING,
321
        self::OPT_REQUEST_COMPRESSION,
322
        self::OPT_RETURN_TYPE,
323
        self::OPT_SSL_VERSION,
324
        self::OPT_TIMEOUT,
325
        self::OPT_USE_CURL,
326
        self::OPT_USER_AGENT,
327
        self::OPT_USERNAME,
328 132
        self::OPT_VERIFY_HOST,
329
        self::OPT_VERIFY_PEER,
330 132
    );
331 132
332
    /**
333
     * @param string $path either the PATH part of the xml-rpc server URL, or complete server URL (in which case you
334
     *                     should use an empty string for all other parameters)
335
     *                     e.g. /xmlrpc/server.php
336
     *                     e.g. http://phpxmlrpc.sourceforge.net/server.php
337
     *                     e.g. https://james:[email protected]:444/xmlrpcserver?agent=007
338 132
     *                     e.g. h2://fast-and-secure-services.org/endpoint
339
     * @param string $server the server name / ip address
340 132
     * @param integer $port the port the server is listening on, when omitted defaults to 80 or 443 depending on
341 132
     *                      protocol used
342
     * @param string $method the http protocol variant: defaults to 'http'; 'https', 'http11', 'h2' and 'h2c' can
343
     *                       be used if CURL is installed. The value set here can be overridden in any call to $this->send().
344
     *                       Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c'
345
     *                       for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be
346
     *                       thus incompatible with any server/proxy not supporting http/2. This is because POST
347
     *                       request are not compatible with h2c upgrade.
348
     */
349
    public function __construct($path, $server = '', $port = '', $method = '')
350
    {
351
        // allow user to specify all params in $path
352
        if ($server == '' && $port == '' && $method == '') {
353
            $parts = parse_url($path);
354
            $server = $parts['host'];
355 99
            $path = isset($parts['path']) ? $parts['path'] : '';
356
            if (isset($parts['query'])) {
357 99
                $path .= '?' . $parts['query'];
358 99
            }
359 99
            if (isset($parts['fragment'])) {
360 99
                $path .= '#' . $parts['fragment'];
361 99
            }
362 99
            if (isset($parts['port'])) {
363
                $port = $parts['port'];
364
            }
365
            if (isset($parts['scheme'])) {
366
                $method = $parts['scheme'];
367
            }
368
            if (isset($parts['user'])) {
369
                $this->username = $parts['user'];
370
            }
371
            if (isset($parts['pass'])) {
372
                $this->password = $parts['pass'];
373
            }
374
        }
375
        if ($path == '' || $path[0] != '/') {
376
            $this->path = '/' . $path;
377
        } else {
378
            $this->path = $path;
379
        }
380
        $this->server = $server;
381
        if ($port != '') {
382
            $this->port = $port;
383
        }
384
        if ($method != '') {
385
            $this->method = $method;
386
        }
387
388
        // if ZLIB is enabled, let the client by default accept compressed responses
389
        if (function_exists('gzinflate') || (
390
                function_exists('curl_version') && (($info = curl_version()) &&
391
                    ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
392
            )
393
        ) {
394
            $this->accepted_compression = array('gzip', 'deflate');
395
        }
396
397
        // keepalives: enabled by default
398
        $this->keepalive = true;
399
400
        // by default the xml parser can support these 3 charset encodings
401
        $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
402
403
        // NB: this is disabled to avoid making all the requests sent huge... mbstring supports more than 80 charsets!
404
        //$ch = Charset::instance();
405
        //$this->accepted_charset_encodings = $ch->knownCharsets();
406
407
        // initialize user_agent string
408
        $this->user_agent = PhpXmlRpc::$xmlrpcName . ' ' . PhpXmlRpc::$xmlrpcVersion;
409
    }
410
411
    /**
412
     * @param string $name
413
     * @param mixed $value
414
     * @return $this
415
     * @throws ValueErrorException on unsupported option
416
     */
417
    public function setOption($name, $value)
418 580
    {
419
        switch ($name) {
420 580
            case self::OPT_ACCEPTED_CHARSET_ENCODINGS:
421 580
                $this->accepted_charset_encodings = $value;
422
                break;
423
            case self::OPT_ACCEPTED_COMPRESSION:
424
                $this->accepted_compression = $value;
425
                break;
426
            case self::OPT_AUTH_TYPE:
427 580
                $this->authtype = $value;
428
                break;
429 580
            case self::OPT_CA_CERT:
430
                $this->cacert = $value;
431
                break;
432
            case self::OPT_CA_CERT_DIR:
433
                $this->cacertdir = $value;
434
                break;
435
            case self::OPT_CERT:
436
                $this->cert = $value;
437
                break;
438
            case self::OPT_CERT_PASS:
439
                $this->certpass = $value;
440
                break;
441
            case self::OPT_COOKIES:
442
                $this->cookies = $value;
443
                break;
444
            case self::OPT_DEBUG:
445
                $this->debug = $value;
446 66
                break;
447
            case self::OPT_EXTRA_CURL_OPTS:
448 66
                $this->extracurlopts = $value;
449 66
                break;
450
            case self::OPT_KEEPALIVE:
451
                $this->keepalive = $value;
452
                break;
453
            case self::OPT_KEY:
454
                $this->key = $value;
455
                break;
456
            case self::OPT_KEY_PASS:
457
                $this->keypass = $value;
458
                break;
459
            case self::OPT_NO_MULTICALL:
460
                $this->no_multicall = $value;
461
                break;
462
            case self::OPT_PASSWORD:
463
                $this->password = $value;
464
                break;
465
            case self::OPT_PROXY:
466
                $this->proxy = $value;
467
                break;
468
            case self::OPT_PROXY_AUTH_TYPE:
469
                $this->proxy_authtype = $value;
470
                break;
471
            case self::OPT_PROXY_PASS:
472
                $this->proxy_pass = $value;
473
                break;
474
            case self::OPT_PROXY_PORT:
475
                $this->proxyport = $value;
476
                break;
477
            case self::OPT_PROXY_USER:
478
                $this->proxy_user = $value;
479
                break;
480
            case self::OPT_REQUEST_CHARSET_ENCODING:
481
                $this->request_charset_encoding = $value;
482
                break;
483
            case self::OPT_REQUEST_COMPRESSION:
484
                $this->request_compression = $value;
485
                break;
486
            case self::OPT_RETURN_TYPE:
487
                $this->return_type = $value;
488
                break;
489
            case self::OPT_SSL_VERSION:
490
                $this->sslversion = $value;
491
                break;
492
            case self::OPT_TIMEOUT:
493
                $this->timeout = $value;
494
                break;
495 697
            case self::OPT_USERNAME:
496
                $this->username = $value;
497
                break;
498
            case self::OPT_USER_AGENT:
499 697
                $this->user_agent = $value;
500 117
                break;
501
            case self::OPT_USE_CURL:
502
                $this->use_curl = $value;
503 697
                break;
504
            case self::OPT_VERIFY_HOST:
505 66
                $this->verifyhost = $value;
506
                break;
507 66
            case self::OPT_VERIFY_PEER:
508 697
                $this->verifypeer = $value;
509 28
                break;
510 28
            default:
511 28
                throw new ValueErrorException("Unsupported option '$name'");
512
        }
513
514
        return $this;
515 697
    }
516
517
    /**
518
     * @param string $name
519 697
     * @return mixed
520 697
     * @throws ValueErrorException on unsupported option
521
     */
522 697
    public function getOption($name)
523 322
    {
524 322
        switch ($name) {
525 322
            case self::OPT_ACCEPTED_CHARSET_ENCODINGS:
526 322
                return $this->accepted_charset_encodings;
527
            case self::OPT_ACCEPTED_COMPRESSION:
528 322
                return $this->accepted_compression;
529 322
            case self::OPT_AUTH_TYPE:
530 322
                return $this->authtype;
531 322
            case self::OPT_CA_CERT:
532 322
                return $this->cacert;
533 322
            case self::OPT_CA_CERT_DIR:
534 322
                return $this->cacertdir;
535 322
            case self::OPT_CERT:
536 322
                return $this->cert;
537 322
            case self::OPT_CERT_PASS:
538 322
                return $this->certpass;
539 322
            case self::OPT_COOKIES:
540
                return $this->cookies;
541 322
            case self::OPT_DEBUG:
542 322
                return $this->debug;
543 322
            case self::OPT_EXTRA_CURL_OPTS:
544 322
                return $this->extracurlopts;
545 322
            case self::OPT_KEEPALIVE:
546
                return $this->keepalive;
547
            case self::OPT_KEY:
548
                return $this->key;
549 375
            case self::OPT_KEY_PASS:
550 375
                return $this->keypass;
551 375
            case self::OPT_NO_MULTICALL:
552 375
                return $this->no_multicall;
553
            case self::OPT_PASSWORD:
554 375
                return $this->password;
555 375
            case self::OPT_PROXY:
556 375
                return $this->proxy;
557 375
            case self::OPT_PROXY_AUTH_TYPE:
558 375
                return $this->proxy_authtype;
559 375
            case self::OPT_PROXY_PASS:
560 375
                return $this->proxy_pass;
561 375
            case self::OPT_PROXY_PORT:
562 375
                return $this->proxyport;
563 375
            case self::OPT_PROXY_USER:
564 375
                return $this->proxy_user;
565 375
            case self::OPT_REQUEST_CHARSET_ENCODING:
566
                return $this->request_charset_encoding;
567 375
            case self::OPT_REQUEST_COMPRESSION:
568 375
                return $this->request_compression;
569 375
            case self::OPT_RETURN_TYPE:
570
                return $this->return_type;
571
            case self::OPT_SSL_VERSION:
572
                return $this->sslversion;
573 697
            case self::OPT_TIMEOUT:
574
                return $this->timeout;
575
            case self::OPT_USERNAME:
576
                return $this->username;
577
            case self::OPT_USER_AGENT:
578
                return $this->user_agent;
579
            case self::OPT_USE_CURL:
580
                return $this->use_curl;
581
            case self::OPT_VERIFY_HOST:
582
                return $this->verifyhost;
583
            case self::OPT_VERIFY_PEER:
584
                return $this->verifypeer;
585
            default:
586
                throw new ValueErrorException("Unsupported option '$name'");
587
        }
588
    }
589
590
    /**
591
     * Returns the complete list of Client options.
592
     * @return array
593
     */
594
    public function getOptions()
595
    {
596
        $values = array();
597
        foreach ($this->options as $opt) {
598
            $values[$opt] = $this->getOption($opt);
599
        }
600
        return $values;
601
    }
602
603
    /**
604
     * @param array $options
605
     * @return $this
606
     * @throws ValueErrorException on unsupported option
607
     */
608
    public function setOptions($options)
609
    {
610
        foreach ($options as $name => $value) {
611
            $this->setOption($name, $value);
612
        }
613
614
        return $this;
615
    }
616
617
    /**
618
     * Enable/disable the echoing to screen of the xml-rpc responses received. The default is not to output anything.
619
     *
620
     * The debugging information at level 1 includes the raw data returned from the XML-RPC server it was querying
621
     * (including bot HTTP headers and the full XML payload), and the PHP value the client attempts to create to
622
     * represent the value returned by the server.
623
     * At level 2, the complete payload of the xml-rpc request is also printed, before being sent to the server.
624
     * At level -1, the Response objects returned by send() calls will not carry information about the http response's
625
     * cookies, headers and body, which might save some memory
626
     *
627
     * This option can be very useful when debugging servers as it allows you to see exactly what the client sends and
628
     * the server returns. Never leave it enabled for production!
629
     *
630
     * @param integer $level values -1, 0, 1 and 2 are supported
631
     * @return $this
632
     */
633
    public function setDebug($level)
634
    {
635
        $this->debug = $level;
636
        return $this;
637
    }
638
639
    /**
640
     * Sets the username and password for authorizing the client to the server.
641
     *
642
     * With the default (HTTP) transport, this information is used for HTTP Basic authorization.
643
     * Note that username and password can also be set using the class constructor.
644
     * With HTTP 1.1 and HTTPS transport, NTLM and Digest authentication protocols are also supported. To enable them use
645
     * the constants CURLAUTH_DIGEST and CURLAUTH_NTLM as values for the auth type parameter.
646
     *
647
     * @param string $user username
648
     * @param string $password password
649
     * @param integer $authType auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC
650
     *                          (basic auth). Note that auth types NTLM and Digest will only work if the Curl php
651
     *                          extension is enabled.
652
     * @return $this
653
     */
654
    public function setCredentials($user, $password, $authType = 1)
655
    {
656
        $this->username = $user;
657
        $this->password = $password;
658
        $this->authtype = $authType;
659
        return $this;
660
    }
661
662
    /**
663
     * Set the optional certificate and passphrase used in SSL-enabled communication with a remote server.
664 375
     *
665
     * Note: to retrieve information about the client certificate on the server side, you will need to look into the
666
     * environment variables which are set up by the webserver. Different webservers will typically set up different
667
     * variables.
668
     *
669
     * @param string $cert the name of a file containing a PEM formatted certificate
670
     * @param string $certPass the password required to use it
671 375
     * @return $this
672 373
     */
673
    public function setCertificate($cert, $certPass = '')
674
    {
675
        $this->cert = $cert;
676 375
        $this->certpass = $certPass;
677 346
        return $this;
678
    }
679
680 375
    /**
681
     * Add a CA certificate to verify server with in SSL-enabled communication when SetSSLVerifypeer has been set to TRUE.
682 375
     *
683 375
     * See the php manual page about CURLOPT_CAINFO for more details.
684 64
     *
685 32
     * @param string $caCert certificate file name (or dir holding certificates)
686 32
     * @param bool $isDir set to true to indicate cacert is a dir. defaults to false
687 32
     * @return $this
688 32
     */
689
    public function setCaCertificate($caCert, $isDir = false)
690
    {
691 32
        if ($isDir) {
692 32
            $this->cacertdir = $caCert;
693 32
        } else {
694 32
            $this->cacert = $caCert;
695
        }
696
        return $this;
697
    }
698
699
    /**
700 375
     * Set attributes for SSL communication: private SSL key.
701 375
     *
702 32
     * NB: does not work in older php/curl installs.
703 32
     * Thanks to Daniel Convissor.
704
     *
705
     * @param string $key The name of a file containing a private SSL key
706
     * @param string $keyPass The secret password needed to use the private SSL key
707
     * @return $this
708 375
     */
709 375
    public function setKey($key, $keyPass)
710 73
    {
711
        $this->key = $key;
712
        $this->keypass = $keyPass;
713 375
        return $this;
714 375
    }
715 32
716
    /**
717
     * Set attributes for SSL communication: verify the remote host's SSL certificate, and cause the connection to fail
718 32
     * if the cert verification fails.
719 32
     *
720 32
     * By default, verification is enabled.
721 32
     * To specify custom SSL certificates to validate the server with, use the setCaCertificate method.
722 32
     *
723
     * @param bool $i enable/disable verification of peer certificate
724
     * @return $this
725
     * @deprecated use setOption
726 32
     */
727
    public function setSSLVerifyPeer($i)
728
    {
729 343
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
730 343
731 343
        $this->verifypeer = $i;
732 343
        return $this;
733
    }
734
735
    /**
736 375
     * Set attributes for SSL communication: verify the remote host's SSL certificate's common name (CN).
737 375
     *
738 309
     * Note that support for value 1 has been removed in cURL 7.28.1
739 309
     *
740 309
     * @param int $i Set to 1 to only the existence of a CN, not that it matches
741
     * @return $this
742
     * @deprecated use setOption
743
     */
744
    public function setSSLVerifyHost($i)
745
    {
746
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
747
748
        $this->verifyhost = $i;
749
        return $this;
750
    }
751
752
    /**
753 309
     * Set attributes for SSL communication: SSL version to use. Best left at 0 (default value): let cURL decide
754
     *
755
     * @param int $i
756 309
     * @return $this
757
     * @deprecated use setOption
758
     */
759
    public function setSSLVersion($i)
760 375
    {
761 375
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
762
763
        $this->sslversion = $i;
764
        return $this;
765
    }
766 375
767 375
    /**
768 375
     * Set proxy info.
769 375
     *
770 375
     * NB: CURL versions before 7.11.10 cannot use a proxy to communicate with https servers.
771 375
     *
772 375
     * @param string $proxyHost
773 375
     * @param string $proxyPort Defaults to 8080 for HTTP and 443 for HTTPS
774 375
     * @param string $proxyUsername Leave blank if proxy has public access
775 375
     * @param string $proxyPassword Leave blank if proxy has public access
776 375
     * @param int $proxyAuthType defaults to CURLAUTH_BASIC (Basic authentication protocol); set to constant CURLAUTH_NTLM
777 375
     *                           to use NTLM auth with proxy (has effect only when the client uses the HTTP 1.1 protocol)
778
     * @return $this
779 375
     */
780 2
    public function setProxy($proxyHost, $proxyPort, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1)
781
    {
782
        $this->proxy = $proxyHost;
783 375
        $this->proxyport = $proxyPort;
0 ignored issues
show
Documentation Bug introduced by
The property $proxyport was declared of type integer, but $proxyPort 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...
784 375
        $this->proxy_user = $proxyUsername;
785 32
        $this->proxy_pass = $proxyPassword;
786
        $this->proxy_authtype = $proxyAuthType;
787
        return $this;
788
    }
789
790
    /**
791 32
     * Enables/disables reception of compressed xml-rpc responses.
792
     *
793
     * This requires the "zlib" extension to be enabled in your php install. If it is, by default xmlrpc_client
794 32
     * instances will enable reception of compressed content.
795
     * Note that enabling reception of compressed responses merely adds some standard http headers to xml-rpc requests.
796
     * It is up to the xml-rpc server to return compressed responses when receiving such requests.
797 32
     *
798
     * @param string $compMethod either 'gzip', 'deflate', 'any' or ''
799
     * @return $this
800 32
     */
801 32
    public function setAcceptedCompression($compMethod)
802
    {
803
        if ($compMethod == 'any') {
804 375
            $this->accepted_compression = array('gzip', 'deflate');
805
        } elseif ($compMethod == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $compMethod of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
806 375
            $this->accepted_compression = array();
807 64
        } else {
808
            $this->accepted_compression = array($compMethod);
809 311
        }
810
        return $this;
811
    }
812 375
813 375
    /**
814
     * Enables/disables http compression of xml-rpc request.
815 375
     *
816 375
     * This requires the "zlib" extension to be enabled in your php install.
817 375
     * Take care when sending compressed requests: servers might not support them (and automatic fallback to
818 374
     * uncompressed requests is not yet implemented).
819 367
     *
820
     * @param string $compMethod either 'gzip', 'deflate' or ''
821
     * @return $this
822 1
     * @deprecated use setOption
823
     */
824
    public function setRequestCompression($compMethod)
825
    {
826
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
827 1
828 1
        $this->request_compression = $compMethod;
829
        return $this;
830 1
    }
831
832
    /**
833 374
     * Adds a cookie to list of cookies that will be sent to server with every further request (useful e.g. for keeping
834
     * session info outside the xml-rpc payload).
835
     *
836
     * NB: by default all cookies set via this method are sent to the server, regardless of path/domain/port. Taking
837
     * advantage of those values is left to the single developer.
838
     *
839
     * @param string $name nb: will not be escaped in the request's http headers. Take care not to use CTL chars or
840
     *                     separators!
841
     * @param string $value
842
     * @param string $path
843 374
     * @param string $domain
844
     * @param int $port do not use! Cookies are not separated by port
845
     * @return $this
846
     *
847 374
     * @todo check correctness of urlencoding cookie value (copied from php way of doing it, but php is generally sending
848 374
     *       response not requests. We do the opposite...)
849 374
     * @todo strip invalid chars from cookie name? As per RFC6265, we should follow RFC2616, Section 2.2
850
     * @todo drop/rename $port parameter. Cookies are not isolated by port!
851 374
     * @todo feature-creep allow storing 'expires', 'secure', 'httponly' and 'samesite' cookie attributes
852
     */
853 374
    public function setCookie($name, $value = '', $path = '', $domain = '', $port = null)
854
    {
855
        $this->cookies[$name]['value'] = rawurlencode($value);
856
        if ($path || $domain || $port) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $port of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
857
            $this->cookies[$name]['path'] = $path;
858
            $this->cookies[$name]['domain'] = $domain;
859
            $this->cookies[$name]['port'] = $port;
860
        }
861
        return $this;
862
    }
863
864
    /**
865
     * Directly set cURL options, for extra flexibility (when in cURL mode).
866
     *
867
     * It allows e.g. to bind client to a specific IP interface / address.
868
     *
869
     * @param array $options
870
     * @return $this
871
     * @deprecated use setOption
872
     */
873
    public function setCurlOptions($options)
874
    {
875
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
876
877
        $this->extracurlopts = $options;
878
        return $this;
879
    }
880
881
    /**
882
     * @param int $useCurlMode self::USE_CURL_ALWAYS, self::USE_CURL_AUTO or self::USE_CURL_NEVER
883
     * @return $this
884
     * @deprecated use setOption
885
     */
886 322
    public function setUseCurl($useCurlMode)
887
    {
888
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
889
890
        $this->use_curl = $useCurlMode;
891 322
        return $this;
892
    }
893
894
895 322
    /**
896
     * Set user-agent string that will be used by this client instance in http headers sent to the server.
897 96
     *
898 96
     * The default user agent string includes the name of this library and the version number.
899
     *
900
     * @param string $agentString
901
     * @return $this
902
     * @deprecated use setOption
903
     */
904 322
    public function setUserAgent($agentString)
905 322
    {
906
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
907
908
        $this->user_agent = $agentString;
909
        return $this;
910 322
    }
911
912
    /**
913
     * @return string
914
     * @todo feature-creep allow to return single url components, such as parse_url does
915 322
     */
916
    public function getUrl()
917 322
    {
918
        $url = $this->method . '://' . $this->server;
919
        if (($this->port = 80 && in_array($this->method, array('http', 'http10', 'http11', 'h2c'))) &&
0 ignored issues
show
Documentation Bug introduced by
The property $port was declared of type integer, but 80 && in_array($this->me...p10', 'http11', 'h2c')) is of type boolean. 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...
920
            ($this->port = 443 && in_array($this->method, array('https', 'h2')))) {
921
            return $url . $this->path;
922
        } else {
923
            return $url . ':' . $this->port . $this->path;
0 ignored issues
show
Bug introduced by
Are you sure $this->port of type 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

923
            return $url . ':' . /** @scrutinizer ignore-type */ $this->port . $this->path;
Loading history...
924
        }
925
    }
926
927
    /**
928
     * Send an xml-rpc request to the server.
929 322
     *
930
     * @param Request|Request[]|string $req The Request object, or an array of requests for using multicall, or the
931
     *                                      complete xml representation of a request.
932 1
     *                                      When sending an array of Request objects, the client will try to make use of
933 1
     *                                      a single 'system.multicall' xml-rpc method call to forward to the server all
934 1
     *                                      the requests in a single HTTP round trip, unless $this->no_multicall has
935 1
     *                                      been previously set to TRUE (see the multicall method below), in which case
936 1
     *                                      many consecutive xml-rpc requests will be sent. The method will return an
937
     *                                      array of Response objects in both cases.
938
     *                                      The third variant allows to build by hand (or any other means) a complete
939 322
     *                                      xml-rpc request message, and send it to the server. $req should be a string
940 160
     *                                      containing the complete xml representation of the request. It is e.g. useful
941
     *                                      when, for maximal speed of execution, the request is serialized into a
942 322
     *                                      string using the native php xml-rpc functions (see http://www.php.net/xmlrpc)
943
     * @param integer $timeout deprecated. Connection timeout, in seconds, If unspecified, the timeout set with setOption
944 322
     *                         will be used. If that is 0, a platform specific timeout will apply.
945
     *                         This timeout value is passed to fsockopen(). It is also used for detecting server
946
     *                         timeouts during communication (i.e. if the server does not send anything to the client
947
     *                         for $timeout seconds, the connection will be closed).
948
     * @param string $method deprecated. Use the same value in the constructor instead.
949
     *                       Valid values are 'http', 'http11', 'https', 'h2' and 'h2c'. If left empty,
950 322
     *                       the http protocol chosen during creation of the object will be used.
951
     *                       Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c'
952
     *                       for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be
953 323
     *                       thus incompatible with any server/proxy not supporting http/2. This is because POST
954
     *                       request are not compatible with h2c upgrade.
955
     * @return Response|Response[] Note that the client will always return a Response object, even if the call fails
956
     *
957
     * @todo allow throwing exceptions instead of returning responses in case of failed calls and/or Fault responses
958 323
     * @todo refactor: we now support many options besides connection timeout and http version to use. Why only privilege those?
959 322
     */
960 226
    public function send($req, $timeout = 0, $method = '')
961
    {
962 96
        if ($method !== '' || $timeout !== 0) {
963
            $this->logDeprecation("Using non-default values for arguments 'method' and 'timeout' when calling method " . __METHOD__ . ' is deprecated');
964
        }
965
966
        // if user does not specify http protocol, use native method of this client
967 323
        // (i.e. method set during call to constructor)
968 302
        if ($method == '') {
969
            $method = $this->method;
970
        }
971
972 323
        if ($timeout == 0) {
973 323
            $timeout = $this->timeout;
974 64
        }
975 32
976 32
        if (is_array($req)) {
977 32
            // $req is an array of Requests
978 32
            return $this->multicall($req, $timeout, $method);
979
        } elseif (is_string($req)) {
980
            $n = new static::$requestClass('');
981 32
            $n->payload = $req;
982 32
            $req = $n;
983 32
        }
984 64
985
        // where req is a Request
986
        $req->setDebug($this->debug);
987
988 259
        /// @todo we could be smarter about this and force usage of curl in scenarios where it is both available and
989
        ///       needed, such as digest or ntlm auth. Do not attempt to use it for https if not present
990
        $useCurl = ($this->use_curl == self::USE_CURL_ALWAYS) || ($this->use_curl == self::USE_CURL_AUTO &&
991 323
                (in_array($method, array('https', 'http11', 'h2c', 'h2'))));
992 323
993 64
        // BC - we go through sendPayloadCURL/sendPayloadSocket in case some subclass reimplemented those
994
        if ($useCurl) {
995 259
            $r = $this->sendPayloadCURL(
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Client::sendPayloadCURL() 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

995
            $r = /** @scrutinizer ignore-deprecated */ $this->sendPayloadCURL(
Loading history...
996 32
                $req,
997
                $this->server,
998
                $this->port,
999 227
                $timeout,
1000
                $this->username,
1001
                $this->password,
1002 323
                $this->authtype,
1003 323
                $this->cert,
1004 322
                $this->certpass,
1005
                $this->cacert,
1006
                $this->cacertdir,
1007 56
                $this->proxy,
1008
                $this->proxyport,
1009
                $this->proxy_user,
1010
                $this->proxy_pass,
1011 323
                $this->proxy_authtype,
1012
                // bc
1013 323
                $method == 'http11' ? 'http' : $method,
1014
                $this->keepalive,
1015
                $this->key,
1016
                $this->keypass,
1017 323
                $this->sslversion
1018
            );
1019 323
        } else {
1020
            $r = $this->sendPayloadSocket(
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Client::sendPayloadSocket() 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

1020
            $r = /** @scrutinizer ignore-deprecated */ $this->sendPayloadSocket(
Loading history...
1021 323
                $req,
1022
                $this->server,
1023
                $this->port,
1024 323
                $timeout,
1025
                $this->username,
1026
                $this->password,
1027
                $this->authtype,
1028 323
                $this->cert,
1029
                $this->certpass,
1030
                $this->cacert,
1031 66
                $this->cacertdir,
1032 64
                $this->proxy,
1033
                $this->proxyport,
1034 2
                $this->proxy_user,
1035
                $this->proxy_pass,
1036
                $this->proxy_authtype,
1037
                $method,
1038 323
                $this->key,
1039
                $this->keypass,
1040 323
                $this->sslversion
1041 161
            );
1042
        }
1043
1044 323
        return $r;
1045 64
    }
1046
1047
    /**
1048
     * @param Request $req
1049
     * @param string $method
1050 323
     * @param string $server
1051
     * @param int $port
1052 323
     * @param string $path
1053
     * @param array $opts
1054 323
     * @return Response
1055 272
     */
1056
    protected function sendViaSocket($req, $method, $server, $port, $path, $opts)
1057
    {
1058 323
        /// @todo log a warning if passed an unsupported method
1059 323
1060 32
        // Only create the payload if it was not created previously
1061 32
        /// @todo what if the request's payload was created with a different encoding?
1062 291
        if (empty($req->payload)) {
1063
            $req->serialize($opts['request_charset_encoding']);
1064
        }
1065 291
        $payload = $req->payload;
1066 32
1067 32
        // Deflate request body and set appropriate request headers
1068 259
        $encodingHdr = '';
1069 32
        if (function_exists('gzdeflate') && ($opts['request_compression'] == 'gzip' || $opts['request_compression'] == 'deflate')) {
1070 32
            if ($opts['request_compression'] == 'gzip') {
1071
                $a = @gzencode($payload);
1072
                if ($a) {
1073 323
                    $payload = $a;
1074 32
                    $encodingHdr = "Content-Encoding: gzip\r\n";
1075 32
                }
1076 32
            } else {
1077
                $a = @gzcompress($payload);
1078
                if ($a) {
1079
                    $payload = $a;
1080
                    $encodingHdr = "Content-Encoding: deflate\r\n";
1081
                }
1082 323
            }
1083
        }
1084 96
1085
        // thanks to Grant Rauscher <[email protected]> for this
1086
        $credentials = '';
1087
        if ($opts['username'] != '') {
1088 96
            $credentials = 'Authorization: Basic ' . base64_encode($opts['username'] . ':' . $opts['password']) . "\r\n";
1089
            if ($opts['authtype'] != 1) {
1090
                /// @todo make this a proper error, i.e. return a failure
1091
                $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported with HTTP 1.0');
1092 96
            }
1093
        }
1094 96
1095
        $acceptedEncoding = '';
1096
        if (is_array($opts['accepted_compression']) && count($opts['accepted_compression'])) {
1097 96
            $acceptedEncoding = 'Accept-Encoding: ' . implode(', ', $opts['accepted_compression']) . "\r\n";
1098
        }
1099
1100
        if ($port == 0) {
1101 96
            $port = ($method === 'https') ? 443 : 80;
1102
        }
1103
1104
        $proxyCredentials = '';
1105 96
        if ($opts['proxy']) {
1106
            if ($opts['proxyport'] == 0) {
1107
                $opts['proxyport'] = 8080;
1108
            }
1109
            $connectServer = $opts['proxy'];
1110 96
            $connectPort = $opts['proxyport'];
1111
            $transport = 'tcp';
1112 96
            /// @todo check: should we not use https in some cases?
1113
            $uri = 'http://' . $server . ':' . $port . $path;
1114
            if ($opts['proxy_user'] != '') {
1115
                if ($opts['proxy_authtype'] != 1) {
1116 323
                    /// @todo make this a proper error, i.e. return a failure
1117 64
                    $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1118
                }
1119
                $proxyCredentials = 'Proxy-Authorization: Basic ' . base64_encode($opts['proxy_user'] . ':' .
1120 64
                    $opts['proxy_pass']) . "\r\n";
1121 64
            }
1122
        } else {
1123
            $connectServer = $server;
1124
            $connectPort = $port;
1125
            $transport = ($method === 'https') ? 'tls' : 'tcp';
1126
            $uri = $path;
1127
        }
1128
1129
        // Cookie generation, as per RFC6265
1130
        // NB: the following code does not honour 'expires', 'path' and 'domain' cookie attributes set to client obj by the user...
1131
        $cookieHeader = '';
1132
        if (count($opts['cookies'])) {
1133 323
            $version = '';
1134 271
            foreach ($opts['cookies'] as $name => $cookie) {
1135 271
                /// @todo should we sanitize the cookie value on behalf of the user? See setCookie comments
1136 271
                $cookieHeader .= ' ' . $name . '=' . $cookie['value'] . ";";
1137
            }
1138 271
            $cookieHeader = 'Cookie:' . $version . substr($cookieHeader, 0, -1) . "\r\n";
1139
        }
1140
1141 323
        // omit port if default
1142
        if (($port == 80 && in_array($method, array('http', 'http10'))) || ($port == 443 && $method == 'https')) {
1143
            $port = '';
1144
        } else {
1145 323
            $port = ':' . $port;
1146
        }
1147
1148
        $op = 'POST ' . $uri . " HTTP/1.0\r\n" .
1149 323
            'User-Agent: ' . $opts['user_agent'] . "\r\n" .
1150
            'Host: ' . $server . $port . "\r\n" .
1151
            $credentials .
1152
            $proxyCredentials .
1153
            $acceptedEncoding .
1154
            $encodingHdr .
1155
            'Accept-Charset: ' . implode(',', $opts['accepted_charset_encodings']) . "\r\n" .
1156
            $cookieHeader .
1157
            'Content-Type: ' . $req->content_type . "\r\nContent-Length: " .
1158
            strlen($payload) . "\r\n\r\n" .
1159
            $payload;
1160
1161
        if ($opts['debug'] > 1) {
1162
            $this->getLogger()->debug("---SENDING---\n$op\n---END---");
1163
        }
1164
1165
        $contextOptions = array();
1166
        if ($method == 'https') {
1167
            if ($opts['cert'] != '') {
1168
                $contextOptions['ssl']['local_cert'] = $opts['cert'];
1169
                if ($opts['certpass'] != '') {
1170
                    $contextOptions['ssl']['passphrase'] = $opts['certpass'];
1171
                }
1172
            }
1173
            if ($opts['cacert'] != '') {
1174 66
                $contextOptions['ssl']['cafile'] = $opts['cacert'];
1175
            }
1176 66
            if ($opts['cacertdir'] != '') {
1177
                $contextOptions['ssl']['capath'] = $opts['cacertdir'];
1178
            }
1179 66
            if ($opts['key'] != '') {
1180 45
                $contextOptions['ssl']['local_pk'] = $opts['key'];
1181 45
            }
1182
            $contextOptions['ssl']['verify_peer'] = $opts['verifypeer'];
1183 45
            $contextOptions['ssl']['verify_peer_name'] = $opts['verifypeer'];
1184
        }
1185
1186
        $context = stream_context_create($contextOptions);
1187
1188
        if ($opts['timeout'] <= 0) {
1189
            $connectTimeout = ini_get('default_socket_timeout');
1190
        } else {
1191
            $connectTimeout = $opts['timeout'];
1192
        }
1193
1194
        $this->errno = 0;
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errno has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1194
        /** @scrutinizer ignore-deprecated */ $this->errno = 0;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1195
        $this->errstr = '';
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1195
        /** @scrutinizer ignore-deprecated */ $this->errstr = '';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1196
1197
        $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr, $connectTimeout,
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errno has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1197
        $fp = @stream_socket_client("$transport://$connectServer:$connectPort", /** @scrutinizer ignore-deprecated */ $this->errno, $this->errstr, $connectTimeout,

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1197
        $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, /** @scrutinizer ignore-deprecated */ $this->errstr, $connectTimeout,

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1198
            STREAM_CLIENT_CONNECT, $context);
1199
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1200
            if ($opts['timeout'] > 0) {
1201 23
                stream_set_timeout($fp, $opts['timeout'], 0);
1202
            }
1203
        } else {
1204 23
            if ($this->errstr == '') {
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1204
            if (/** @scrutinizer ignore-deprecated */ $this->errstr == '') {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1205 23
                $err = error_get_last();
1206
                $this->errstr = $err['message'];
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1206
                /** @scrutinizer ignore-deprecated */ $this->errstr = $err['message'];

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1207
            }
1208
1209 23
            $this->errstr = 'Connect error: ' . $this->errstr;
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1209
            $this->errstr = 'Connect error: ' . /** @scrutinizer ignore-deprecated */ $this->errstr;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1210 23
            $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . $this->errno . ')');
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errno has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1210
            $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . /** @scrutinizer ignore-deprecated */ $this->errno . ')');

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1210
            $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], /** @scrutinizer ignore-deprecated */ $this->errstr . ' (' . $this->errno . ')');

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1211
1212
            return $r;
1213
        }
1214
1215
        if (!fputs($fp, $op, strlen($op))) {
1216
            fclose($fp);
1217
            $this->errstr = 'Write error';
1218
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr);
1219
        }
1220
1221 23
        // Close socket before parsing.
1222
        // It should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1223
        $ipd = '';
1224
        do {
1225
            // shall we check for $data === FALSE?
1226
            // as per the manual, it signals an error
1227
            $ipd .= fread($fp, 32768);
1228
        } while (!feof($fp));
1229
        fclose($fp);
1230
1231
        return $req->parseResponse($ipd, false, $opts['return_type']);
1232
    }
1233
1234
    /**
1235 45
     * Contributed by Justin Miller <[email protected]>
1236
     * Requires curl to be built into PHP
1237
     * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1238 45
     *
1239 45
     * @param Request $req
1240 45
     * @param string $method
1241 45
     * @param string $server
1242 45
     * @param int $port
1243 45
     * @param string $path
1244 45
     * @param array $opts the keys/values match self::getOptions
1245
     * @return Response
1246 45
     *
1247 45
     * @todo the $path arg atm is ignored. What to do if it is != $this->path?
1248
     */
1249 45
    protected function sendViaCURL($req, $method, $server, $port, $path, $opts)
0 ignored issues
show
Unused Code introduced by
The parameter $path 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

1249
    protected function sendViaCURL($req, $method, $server, $port, /** @scrutinizer ignore-unused */ $path, $opts)

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...
1250 45
    {
1251
        if (!function_exists('curl_init')) {
1252
            $this->errstr = 'CURL unavailable on this install';
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1252
            /** @scrutinizer ignore-deprecated */ $this->errstr = 'CURL unavailable on this install';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1253 45
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_curl'], PhpXmlRpc::$xmlrpcstr['no_curl']);
1254
        }
1255 45
        if ($method == 'https' || $method == 'h2') {
1256
            // q: what about installs where we get back a string, but curl is linked to other ssl libs than openssl?
1257
            if (($info = curl_version()) &&
1258
                ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version'])))
0 ignored issues
show
introduced by
The condition is_string($info) is always false.
Loading history...
1259
            ) {
1260
                $this->errstr = 'SSL unavailable on this install';
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1260
                /** @scrutinizer ignore-deprecated */ $this->errstr = 'SSL unavailable on this install';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1261 45
                return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_ssl'], PhpXmlRpc::$xmlrpcstr['no_ssl']);
1262
            }
1263 45
        }
1264
        if (($method == 'h2' && !defined('CURL_HTTP_VERSION_2_0')) ||
1265 45
            ($method == 'h2c' && !defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))) {
1266
            $this->errstr = 'HTTP/2 unavailable on this install';
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1266
            /** @scrutinizer ignore-deprecated */ $this->errstr = 'HTTP/2 unavailable on this install';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1267 22
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_http2'], PhpXmlRpc::$xmlrpcstr['no_http2']);
1268 22
        }
1269
1270
        // BC - we go through prepareCurlHandle in case some subclass reimplemented it
1271 22
        $curl = $this->prepareCurlHandle($req, $server, $port, $opts['timeout'], $opts['username'], $opts['password'],
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Client::prepareCurlHandle() 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

1271
        $curl = /** @scrutinizer ignore-deprecated */ $this->prepareCurlHandle($req, $server, $port, $opts['timeout'], $opts['username'], $opts['password'],
Loading history...
1272 22
            $opts['authtype'], $opts['cert'], $opts['certpass'], $opts['cacert'], $opts['cacertdir'], $opts['proxy'],
1273
            $opts['proxyport'], $opts['proxy_user'], $opts['proxy_pass'], $opts['proxy_authtype'], $method,
1274
            $opts['keepalive'], $opts['key'], $opts['keypass'], $opts['sslversion']);
1275
1276 22
        if (!$curl) {
1277 22
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] .
1278 22
                ': error during curl initialization. Check php error log for details');
1279 22
        }
1280
1281
        $result = curl_exec($curl);
1282 22
1283 22
        if ($opts['debug'] > 1) {
1284 22
            $message = "---CURL INFO---\n";
1285
            foreach (curl_getinfo($curl) as $name => $val) {
1286
                if (is_array($val)) {
1287
                    $val = implode("\n", $val);
1288 22
                }
1289 22
                $message .= $name . ': ' . $val . "\n";
1290 22
            }
1291
            $message .= '---END---';
1292 22
            $this->getLogger()->debug($message);
1293 22
        }
1294
1295
        if (!$result) {
1296 22
            /// @todo we should use a better check here - what if we get back '' or '0'?
1297 22
1298
            $this->errstr = 'no response';
0 ignored issues
show
Deprecated Code introduced by
The property PhpXmlRpc\Client::$errstr has been deprecated: will be removed in the future ( Ignorable by Annotation )

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

1298
            /** @scrutinizer ignore-deprecated */ $this->errstr = 'no response';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1299
            $resp = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] .
1300 22
                ': ' . curl_error($curl));
1301 22
            curl_close($curl);
1302
            if ($opts['keepalive']) {
1303
                $this->xmlrpc_curl_handle = null;
1304
            }
1305
        } else {
1306
            if (!$opts['keepalive']) {
1307 22
                curl_close($curl);
1308
            }
1309
            $resp = $req->parseResponse($result, true, $opts['return_type']);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $data of PhpXmlRpc\Request::parseResponse() 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

1309
            $resp = $req->parseResponse(/** @scrutinizer ignore-type */ $result, true, $opts['return_type']);
Loading history...
1310
            if ($opts['keepalive']) {
1311 24
                /// @todo if we got back a 302 or 308, we should not reuse the curl handle for later calls
1312 24
                if ($resp->faultCode() == PhpXmlRpc::$xmlrpcerr['http_error']) {
1313
                    curl_close($curl);
1314
                    $this->xmlrpc_curl_handle = null;
1315 24
                }
1316 24
            }
1317
        }
1318
1319
        return $resp;
1320 24
    }
1321 24
1322 24
    /**
1323 24
     * @param Request $req
1324 24
     * @param string $method
1325
     * @param string $server
1326
     * @param int $port
1327
     * @param string $path
1328 24
     * @param array $opts the keys/values match self::getOptions
1329 24
     * @return \CurlHandle|resource|false
1330 22
     */
1331 22
    protected function createCurlHandle($req, $method, $server, $port, $path, $opts)
1332
    {
1333 22
        if ($port == 0) {
1334
            if (in_array($method, array('http', 'http10', 'http11', 'h2c'))) {
1335
                $port = 80;
1336 22
            } else {
1337
                $port = 443;
1338 22
            }
1339
        }
1340
1341 22
        // Only create the payload if it was not created previously
1342 22
        if (empty($req->payload)) {
1343
            $req->serialize($opts['request_charset_encoding']);
1344
        }
1345
1346
        // Deflate request body and set appropriate request headers
1347
        $payload = $req->payload;
1348 24
        $encodingHdr = '';
1349
        /// @todo test for existence of proper function, in case of polyfills
1350
        if (function_exists('gzdeflate') && ($opts['request_compression'] == 'gzip' || $opts['request_compression'] == 'deflate')) {
1351
            if ($opts['request_compression'] == 'gzip') {
1352
                $a = @gzencode($payload);
1353
                if ($a) {
1354
                    $payload = $a;
1355
                    $encodingHdr = 'Content-Encoding: gzip';
1356
                }
1357
            } else {
1358
                $a = @gzcompress($payload);
1359
                if ($a) {
1360
                    $payload = $a;
1361
                    $encodingHdr = 'Content-Encoding: deflate';
1362
                }
1363
            }
1364
        }
1365
1366
        if (!$opts['keepalive'] || !$this->xmlrpc_curl_handle) {
1367
            if ($method == 'http11' || $method == 'http10' || $method == 'h2c') {
1368
                $protocol = 'http';
1369
            } else {
1370
                if ($method == 'h2') {
1371
                    $protocol = 'https';
1372
                } else {
1373
                    // http, https
1374
                    $protocol = $method;
1375
                    if (strpos($protocol, ':') !== false) {
1376
                        $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ": warning - attempted hacking attempt?. The curl protocol requested for the call is: '$protocol'");
1377
                        return false;
1378
                    }
1379
                }
1380
            }
1381
            $curl = curl_init($protocol . '://' . $server . ':' . $port . $path);
1382
            if (!$curl) {
1383
                return false;
1384
            }
1385
            if ($opts['keepalive']) {
1386
                $this->xmlrpc_curl_handle = $curl;
1387
            }
1388
        } else {
1389
            $curl = $this->xmlrpc_curl_handle;
1390
        }
1391
1392
        // results into variable
1393
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
1394
1395
        if ($opts['debug'] > 1) {
1396
            curl_setopt($curl, CURLOPT_VERBOSE, true);
1397
            /// @todo redirect curlopt_stderr to some stream which can be piped to the logger
1398
        }
1399
        curl_setopt($curl, CURLOPT_USERAGENT, $opts['user_agent']);
1400
        // required for XMLRPC: post the data
1401
        curl_setopt($curl, CURLOPT_POST, 1);
1402
        // the data
1403
        curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1404
1405
        // return the header too
1406
        curl_setopt($curl, CURLOPT_HEADER, 1);
1407
1408
        // NB: if we set an empty string, CURL will add http header indicating
1409
        // ALL methods it is supporting. This is possibly a better option than letting the user tell what curl can / cannot do...
1410
        if (is_array($opts['accepted_compression']) && count($opts['accepted_compression'])) {
1411
            //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $opts['accepted_compression']));
1412
            // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1413
            if (count($opts['accepted_compression']) == 1) {
1414
                curl_setopt($curl, CURLOPT_ENCODING, $opts['accepted_compression'][0]);
1415
            } else {
1416
                curl_setopt($curl, CURLOPT_ENCODING, '');
1417
            }
1418
        }
1419
        // extra headers
1420
        $headers = array('Content-Type: ' . $req->content_type, 'Accept-Charset: ' . implode(',', $opts['accepted_charset_encodings']));
1421
        // if no keepalive is wanted, let the server know it in advance
1422
        if (!$opts['keepalive']) {
1423
            $headers[] = 'Connection: close';
1424
        }
1425
        // request compression header
1426
        if ($encodingHdr) {
1427
            $headers[] = $encodingHdr;
1428
        }
1429
1430
        // Fix the HTTP/1.1 417 Expectation Failed Bug (curl by default adds a 'Expect: 100-continue' header when POST
1431
        // size exceeds 1025 bytes, apparently)
1432
        $headers[] = 'Expect:';
1433
1434
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1435
        // timeout is borked
1436
        if ($opts['timeout']) {
1437
            curl_setopt($curl, CURLOPT_TIMEOUT, $opts['timeout'] == 1 ? 1 : $opts['timeout'] - 1);
1438
        }
1439
1440
        switch ($method) {
1441
            case 'http10':
1442
                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
1443
                break;
1444
            case 'http11':
1445
                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1446
                break;
1447
            case 'h2c':
1448
                if (defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE')) {
1449
                    curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
1450
                } else {
1451
                    /// @todo make this a proper error, i.e. return a failure
1452
                    $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. HTTP2 is not supported by the current PHP/curl install');
1453
                }
1454
                break;
1455
            case 'h2':
1456
                curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
1457
                break;
1458
        }
1459
1460
        if ($opts['username'] && $opts['password']) {
1461
            curl_setopt($curl, CURLOPT_USERPWD, $opts['username'] . ':' . $opts['password']);
1462
            if (defined('CURLOPT_HTTPAUTH')) {
1463
                curl_setopt($curl, CURLOPT_HTTPAUTH, $opts['authtype']);
1464
            } elseif ($opts['authtype'] != 1) {
1465
                /// @todo make this a proper error, i.e. return a failure
1466
                $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported by the current PHP/curl install');
1467
            }
1468
        }
1469
1470
        // note: h2c is http2 without the https. No need to have it in this IF
1471
        if ($method == 'https' || $method == 'h2') {
1472
            // set cert file
1473
            if ($opts['cert']) {
1474
                curl_setopt($curl, CURLOPT_SSLCERT, $opts['cert']);
1475
            }
1476
            // set cert password
1477
            if ($opts['certpass']) {
1478
                curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $opts['certpass']);
1479
            }
1480
            // whether to verify remote host's cert
1481
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $opts['verifypeer']);
1482
            // set ca certificates file/dir
1483
            if ($opts['cacert']) {
1484
                curl_setopt($curl, CURLOPT_CAINFO, $opts['cacert']);
1485
            }
1486
            if ($opts['cacertdir']) {
1487
                curl_setopt($curl, CURLOPT_CAPATH, $opts['cacertdir']);
1488
            }
1489
            // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1490
            if ($opts['key']) {
1491
                curl_setopt($curl, CURLOPT_SSLKEY, $opts['key']);
1492
            }
1493
            // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1494
            if ($opts['keypass']) {
1495
                curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $opts['keypass']);
1496
            }
1497
            // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that
1498
            // it matches the hostname used
1499
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $opts['verifyhost']);
1500
            // allow usage of different SSL versions
1501
            curl_setopt($curl, CURLOPT_SSLVERSION, $opts['sslversion']);
1502
        }
1503
1504
        // proxy info
1505
        if ($opts['proxy']) {
1506
            if ($opts['proxyport'] == 0) {
1507
                $opts['proxyport'] = 8080; // NB: even for HTTPS, local connection is on port 8080
1508
            }
1509
            curl_setopt($curl, CURLOPT_PROXY, $opts['proxy'] . ':' . $opts['proxyport']);
1510
            if ($opts['proxy_user']) {
1511
                curl_setopt($curl, CURLOPT_PROXYUSERPWD, $opts['proxy_user'] . ':' . $opts['proxy_pass']);
1512
                if (defined('CURLOPT_PROXYAUTH')) {
1513
                    curl_setopt($curl, CURLOPT_PROXYAUTH, $opts['proxy_authtype']);
1514
                } elseif ($opts['proxy_authtype'] != 1) {
1515
                    /// @todo make this a proper error, i.e. return a failure
1516
                    $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1517
                }
1518
            }
1519
        }
1520
1521
        // NB: should we build cookie http headers by hand rather than let CURL do it?
1522
        // NB: the following code does not honour 'expires', 'path' and 'domain' cookie attributes set to client obj by the user...
1523
        if (count($opts['cookies'])) {
1524
            $cookieHeader = '';
1525
            foreach ($opts['cookies'] as $name => $cookie) {
1526
                $cookieHeader .= $name . '=' . $cookie['value'] . '; ';
1527
            }
1528
            curl_setopt($curl, CURLOPT_COOKIE, substr($cookieHeader, 0, -2));
1529
        }
1530
1531
        foreach ($opts['extracurlopts'] as $opt => $val) {
1532
            curl_setopt($curl, $opt, $val);
1533
        }
1534
1535
        if ($opts['debug'] > 1) {
1536
            $this->getLogger()->debug("---SENDING---\n$payload\n---END---");
1537
        }
1538
1539
        return $curl;
1540
    }
1541
1542
    /**
1543
     * Send an array of requests and return an array of responses.
1544
     *
1545
     * Unless $this->no_multicall has been set to true, it will try first to use one single xml-rpc call to server method
1546
     * system.multicall, and revert to sending many successive calls in case of failure.
1547
     * This failure is also stored in $this->no_multicall for subsequent calls.
1548
     * Unfortunately, there is no server error code universally used to denote the fact that multicall is unsupported,
1549
     * so there is no way to reliably distinguish between that and a temporary failure.
1550
     * If you are sure that server supports multicall and do not want to fallback to using many single calls, set the
1551
     * fourth parameter to FALSE.
1552
     *
1553
     * NB: trying to shoehorn extra functionality into existing syntax has resulted
1554
     * in pretty much convoluted code...
1555
     *
1556
     * @param Request[] $reqs an array of Request objects
1557
     * @param integer $timeout deprecated - connection timeout (in seconds). See the details in the docs for the send() method
1558
     * @param string $method deprecated - the http protocol variant to be used. See the details in the docs for the send() method
1559
     * @param boolean $fallback When true, upon receiving an error during multicall, multiple single calls will be
1560
     *                         attempted
1561
     * @return Response[]
1562
     */
1563
    public function multicall($reqs, $timeout = 0, $method = '', $fallback = true)
1564
    {
1565
        if ($method == '') {
1566
            $method = $this->method;
1567
        }
1568
1569
        if (!$this->no_multicall) {
1570
            $results = $this->_try_multicall($reqs, $timeout, $method);
1571
            /// @todo how to handle the case of $this->return_type = xml?
1572
            if (is_array($results)) {
1573
                // System.multicall succeeded
1574
                return $results;
1575
            } else {
1576
                // either system.multicall is unsupported by server, or the call failed for some other reason.
1577
                // Feature creep: is there a way to tell apart unsupported multicall from other faults?
1578
                if ($fallback) {
1579
                    // Don't try it next time...
1580
                    $this->no_multicall = true;
1581
                } else {
1582
                    $result = $results;
1583
                }
1584
            }
1585
        } else {
1586
            // override fallback, in case careless user tries to do two
1587
            // opposite things at the same time
1588
            $fallback = true;
1589
        }
1590
1591
        $results = array();
1592
        if ($fallback) {
1593
            // system.multicall is (probably) unsupported by server: emulate multicall via multiple requests
1594
            /// @todo use curl multi_ functions to make this quicker (see the implementation in the parallel.php demo)
1595
            foreach ($reqs as $req) {
1596
                $results[] = $this->send($req, $timeout, $method);
1597
            }
1598
        } else {
1599
            // user does NOT want to fallback on many single calls: since we should always return an array of responses,
1600
            // we return an array with the same error repeated n times
1601
            foreach ($reqs as $req) {
1602
                $results[] = $result;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
1603
            }
1604
        }
1605
1606
        return $results;
1607
    }
1608
1609
    /**
1610
     * Attempt to boxcar $reqs via system.multicall.
1611
     *
1612
     * @param Request[] $reqs
1613
     * @param int $timeout
1614
     * @param string $method
1615
     * @return Response[]|Response a single Response when the call returned a fault / does not conform to what we expect
1616
     *                             from a multicall response
1617
     */
1618
    private function _try_multicall($reqs, $timeout, $method)
1619
    {
1620
        // Construct multicall request
1621
        $calls = array();
1622
        foreach ($reqs as $req) {
1623
            $call['methodName'] = new Value($req->method(), 'string');
1624
            $numParams = $req->getNumParams();
1625
            $params = array();
1626
            for ($i = 0; $i < $numParams; $i++) {
1627
                $params[$i] = $req->getParam($i);
1628
            }
1629
            $call['params'] = new Value($params, 'array');
1630
            $calls[] = new Value($call, 'struct');
1631
        }
1632
        $multiCall = new Request('system.multicall');
1633
        $multiCall->addParam(new Value($calls, 'array'));
1634
1635
        // Attempt RPC call
1636
        $result = $this->send($multiCall, $timeout, $method);
1637
1638
        if ($result->faultCode() != 0) {
1639
            // call to system.multicall failed
1640
            return $result;
1641
        }
1642
1643
        // Unpack responses.
1644
        $rets = $result->value();
1645
        $response = array();
1646
1647
        if ($this->return_type == 'xml') {
1648
            for ($i = 0; $i < count($reqs); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1649
                $response[] = new Response($rets, 0, '', 'xml', $result->httpResponse());
1650
            }
1651
1652
        } elseif ($this->return_type == 'phpvals') {
1653
            if (!is_array($rets)) {
0 ignored issues
show
introduced by
The condition is_array($rets) is always false.
Loading history...
1654
                // bad return type from system.multicall
1655
                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1656
                    PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': not an array', 'phpvals', $result->httpResponse());
1657
            }
1658
            $numRets = count($rets);
1659
            if ($numRets != count($reqs)) {
1660
                // wrong number of return values.
1661
                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1662
                    PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'phpvals',
1663
                    $result->httpResponse());
1664
            }
1665
1666
            for ($i = 0; $i < $numRets; $i++) {
1667
                $val = $rets[$i];
1668
                if (!is_array($val)) {
1669
                    return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1670
                        PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct",
1671
                        'phpvals', $result->httpResponse());
1672
                }
1673
                switch (count($val)) {
1674
                    case 1:
1675
                        if (!isset($val[0])) {
1676
                            // Bad value
1677
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1678
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has no value",
1679
                                'phpvals', $result->httpResponse());
1680
                        }
1681
                        // Normal return value
1682
                        $response[$i] = new Response($val[0], 0, '', 'phpvals', $result->httpResponse());
1683
                        break;
1684
                    case 2:
1685
                        /// @todo remove usage of @: it is apparently quite slow
1686
                        $code = @$val['faultCode'];
1687
                        if (!is_int($code)) {
1688
                            /// @todo should we check that it is != 0?
1689
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1690
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
1691
                                'phpvals', $result->httpResponse());
1692
                        }
1693
                        $str = @$val['faultString'];
1694
                        if (!is_string($str)) {
1695
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1696
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no FaultString",
1697
                                'phpvals', $result->httpResponse());
1698
                        }
1699
                        $response[$i] = new Response(0, $code, $str, 'phpvals', $result->httpResponse());
1700
                        break;
1701
                    default:
1702
                        return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1703
                            PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
1704
                            'phpvals', $result->httpResponse());
1705
                }
1706
            }
1707
1708
        } else {
1709
            // return type == 'xmlrpcvals'
1710
            if ($rets->kindOf() != 'array') {
0 ignored issues
show
Bug introduced by
The method kindOf() does not exist on integer. ( Ignorable by Annotation )

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

1710
            if ($rets->/** @scrutinizer ignore-call */ kindOf() != 'array') {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1711
                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1712
                    PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array", 'xmlrpcvals',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $i seems to be defined by a foreach iteration on line 1622. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1713
                    $result->httpResponse());
1714
            }
1715
            $numRets = $rets->count();
1716
            if ($numRets != count($reqs)) {
1717
                // wrong number of return values.
1718
                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1719
                    PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'xmlrpcvals',
1720
                    $result->httpResponse());
1721
            }
1722
1723
            foreach ($rets as $i => $val) {
0 ignored issues
show
Bug introduced by
The expression $rets of type integer is not traversable.
Loading history...
1724
                switch ($val->kindOf()) {
1725
                    case 'array':
1726
                        if ($val->count() != 1) {
1727
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1728
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
1729
                                'phpvals', $result->httpResponse());
1730
                        }
1731
                        // Normal return value
1732
                        $response[] = new Response($val[0], 0, '', 'xmlrpcvals', $result->httpResponse());
1733
                        break;
1734
                    case 'struct':
1735
                        if ($val->count() != 2) {
1736
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1737
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
1738
                                'phpvals', $result->httpResponse());
1739
                        }
1740
                        /** @var Value $code */
1741
                        $code = $val['faultCode'];
1742
                        if ($code->kindOf() != 'scalar' || $code->scalarTyp() != 'int') {
1743
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1744
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
1745
                                'xmlrpcvals', $result->httpResponse());
1746
                        }
1747
                        /** @var Value $str */
1748
                        $str = $val['faultString'];
1749
                        if ($str->kindOf() != 'scalar' || $str->scalarTyp() != 'string') {
1750
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1751
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
1752
                                'xmlrpcvals', $result->httpResponse());
1753
                        }
1754
                        $response[] = new Response(0, $code->scalarVal(), $str->scalarVal(), 'xmlrpcvals', $result->httpResponse());
1755
                        break;
1756
                    default:
1757
                        return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1758
                            PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct",
1759
                            'xmlrpcvals', $result->httpResponse());
1760
                }
1761
            }
1762
        }
1763
1764
        return $response;
1765
    }
1766
1767
    // BC layer
1768
1769
1770
    /**
1771
     * @param Request $req
1772
     * @param string $server
1773
     * @param int $port
1774
     * @param int $timeout
1775
     * @param string $username
1776
     * @param string $password
1777
     * @param int $authType
1778
     * @param string $proxyHost
1779
     * @param int $proxyPort
1780
     * @param string $proxyUsername
1781
     * @param string $proxyPassword
1782
     * @param int $proxyAuthType
1783
     * @param string $method
1784
     * @return Response
1785
     * @deprecated
1786
     *
1787
     */
1788
    protected function sendPayloadHTTP10($req, $server, $port, $timeout = 0, $username = '', $password = '',
1789
        $authType = 1, $proxyHost = '', $proxyPort = 0, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1,
1790
        $method = 'http')
1791
    {
1792
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
1793
1794
        return $this->sendPayloadSocket($req, $server, $port, $timeout, $username, $password, $authType, null, null,
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Client::sendPayloadSocket() 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

1794
        return /** @scrutinizer ignore-deprecated */ $this->sendPayloadSocket($req, $server, $port, $timeout, $username, $password, $authType, null, null,
Loading history...
1795
            null, null, $proxyHost, $proxyPort, $proxyUsername, $proxyPassword, $proxyAuthType, $method);
1796
    }
1797
1798
    /**
1799
     * @param Request $req
1800
     * @param string $server
1801
     * @param int $port
1802
     * @param int $timeout
1803
     * @param string $username
1804
     * @param string $password
1805
     * @param int $authType
1806
     * @param string $cert
1807
     * @param string $certPass
1808
     * @param string $caCert
1809
     * @param string $caCertDir
1810
     * @param string $proxyHost
1811
     * @param int $proxyPort
1812
     * @param string $proxyUsername
1813
     * @param string $proxyPassword
1814
     * @param int $proxyAuthType
1815
     * @param bool $keepAlive
1816
     * @param string $key
1817
     * @param string $keyPass
1818
     * @param int $sslVersion
1819
     * @return Response
1820
     * @deprecated
1821
     *
1822
     */
1823
    protected function sendPayloadHTTPS($req, $server, $port, $timeout = 0, $username = '', $password = '',
1824
        $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0,
1825
        $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $keepAlive = false, $key = '', $keyPass = '',
1826
        $sslVersion = 0)
1827
    {
1828
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
1829
1830
        return $this->sendPayloadCURL($req, $server, $port, $timeout, $username,
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Client::sendPayloadCURL() 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

1830
        return /** @scrutinizer ignore-deprecated */ $this->sendPayloadCURL($req, $server, $port, $timeout, $username,
Loading history...
1831
            $password, $authType, $cert, $certPass, $caCert, $caCertDir, $proxyHost, $proxyPort,
1832
            $proxyUsername, $proxyPassword, $proxyAuthType, 'https', $keepAlive, $key, $keyPass, $sslVersion);
1833
    }
1834
1835
    /**
1836
     * @deprecated
1837
     *
1838
     * @param Request $req
1839
     * @param string $server
1840
     * @param int $port
1841
     * @param int $timeout
1842
     * @param string $username
1843
     * @param string $password
1844
     * @param int $authType only value supported is 1
1845
     * @param string $cert
1846
     * @param string $certPass
1847
     * @param string $caCert
1848
     * @param string $caCertDir
1849
     * @param string $proxyHost
1850
     * @param int $proxyPort
1851
     * @param string $proxyUsername
1852
     * @param string $proxyPassword
1853
     * @param int $proxyAuthType only value supported is 1
1854
     * @param string $method 'http' (synonym for 'http10'), 'http10' or 'https'
1855
     * @param string $key
1856
     * @param string $keyPass @todo not implemented yet.
1857
     * @param int $sslVersion @todo not implemented yet. See http://php.net/manual/en/migration56.openssl.php
1858
     * @return Response
1859
     */
1860
    protected function sendPayloadSocket($req, $server, $port, $timeout = 0, $username = '', $password = '',
1861
        $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0,
1862
        $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'http', $key = '', $keyPass = '',
1863
        $sslVersion = 0)
1864
    {
1865
        return $this->sendViaSocket($req, $method, $server, $port, $this->path, array(
1866
            'accepted_charset_encodings' => $this->accepted_charset_encodings,
1867
            'accepted_compression' => $this->accepted_compression,
1868
            'authtype' => $authType,
1869
            'cacert' => $caCert,
1870
            'cacertdir' => $caCertDir,
1871
            'cert' => $cert,
1872
            'certpass' => $certPass,
1873
            'cookies' => $this->cookies,
1874
            'debug' => $this->debug,
1875
            'extracurlopts' => $this->extracurlopts,
1876
            'keepalive' => $this->keepalive,
1877
            'key' => $key,
1878
            'keypass' => $keyPass,
1879
            'no_multicall' => $this->no_multicall,
1880
            'password' => $password,
1881
            'proxy' => $proxyHost,
1882
            'proxy_authtype' => $proxyAuthType,
1883
            'proxy_pass' => $proxyPassword,
1884
            'proxyport' => $proxyPort,
1885
            'proxy_user' => $proxyUsername,
1886
            'request_charset_encoding' => $this->request_charset_encoding,
1887
            'request_compression' => $this->request_compression,
1888
            'return_type' => $this->return_type,
1889
            'sslversion' => $sslVersion,
1890
            'timeout' => $timeout,
1891
            'username' => $username,
1892
            'user_agent' => $this->user_agent,
1893
            'use_curl' => $this->use_curl,
1894
            'verifyhost' => $this->verifyhost,
1895
            'verifypeer' => $this->verifypeer,
1896
        ));
1897
    }
1898
1899
    /**
1900
     * @param Request $req
1901
     * @param string $server
1902
     * @param int $port
1903
     * @param int $timeout
1904
     * @param string $username
1905
     * @param string $password
1906
     * @param int $authType
1907
     * @param string $cert
1908
     * @param string $certPass
1909
     * @param string $caCert
1910
     * @param string $caCertDir
1911
     * @param string $proxyHost
1912
     * @param int $proxyPort
1913
     * @param string $proxyUsername
1914
     * @param string $proxyPassword
1915
     * @param int $proxyAuthType
1916
     * @param string $method 'http' (let curl decide), 'http10', 'http11', 'https', 'h2c' or 'h2'
1917
     * @param bool $keepAlive
1918
     * @param string $key
1919
     * @param string $keyPass
1920
     * @param int $sslVersion
1921
     * @return Response
1922
     * @deprecated
1923
     *
1924
     */
1925
    protected function sendPayloadCURL($req, $server, $port, $timeout = 0, $username = '', $password = '',
1926
        $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0,
1927
        $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'https', $keepAlive = false, $key = '',
1928
        $keyPass = '', $sslVersion = 0)
1929
    {
1930
        return $this->sendViaCURL($req, $method, $server, $port, $this->path, array(
1931
            'accepted_charset_encodings' => $this->accepted_charset_encodings,
1932
            'accepted_compression' => $this->accepted_compression,
1933
            'authtype' => $authType,
1934
            'cacert' => $caCert,
1935
            'cacertdir' => $caCertDir,
1936
            'cert' => $cert,
1937
            'certpass' => $certPass,
1938
            'cookies' => $this->cookies,
1939
            'debug' => $this->debug,
1940
            'extracurlopts' => $this->extracurlopts,
1941
            'keepalive' => $keepAlive,
1942
            'key' => $key,
1943
            'keypass' => $keyPass,
1944
            'no_multicall' => $this->no_multicall,
1945
            'password' => $password,
1946
            'proxy' => $proxyHost,
1947
            'proxy_authtype' => $proxyAuthType,
1948
            'proxy_pass' => $proxyPassword,
1949
            'proxyport' => $proxyPort,
1950
            'proxy_user' => $proxyUsername,
1951
            'request_charset_encoding' => $this->request_charset_encoding,
1952
            'request_compression' => $this->request_compression,
1953
            'return_type' => $this->return_type,
1954
            'sslversion' => $sslVersion,
1955
            'timeout' => $timeout,
1956
            'username' => $username,
1957
            'user_agent' => $this->user_agent,
1958
            'use_curl' => $this->use_curl,
1959
            'verifyhost' => $this->verifyhost,
1960
            'verifypeer' => $this->verifypeer,
1961
        ));
1962
    }
1963
1964
    /**
1965
     * @deprecated
1966
     *
1967
     * @param $req
1968
     * @param $server
1969
     * @param $port
1970
     * @param $timeout
1971
     * @param $username
1972
     * @param $password
1973
     * @param $authType
1974
     * @param $cert
1975
     * @param $certPass
1976
     * @param $caCert
1977
     * @param $caCertDir
1978
     * @param $proxyHost
1979
     * @param $proxyPort
1980
     * @param $proxyUsername
1981
     * @param $proxyPassword
1982
     * @param $proxyAuthType
1983
     * @param $method
1984
     * @param $keepAlive
1985
     * @param $key
1986
     * @param $keyPass
1987
     * @param $sslVersion
1988
     * @return false|\CurlHandle|resource
1989
     */
1990
    protected function prepareCurlHandle($req, $server, $port, $timeout = 0, $username = '', $password = '',
1991
         $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0,
1992
         $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'https', $keepAlive = false, $key = '',
1993
         $keyPass = '', $sslVersion = 0)
1994
    {
1995
        return $this->createCurlHandle($req, $method, $server, $port, $this->path, array(
1996
            'accepted_charset_encodings' => $this->accepted_charset_encodings,
1997
            'accepted_compression' => $this->accepted_compression,
1998
            'authtype' => $authType,
1999
            'cacert' => $caCert,
2000
            'cacertdir' => $caCertDir,
2001
            'cert' => $cert,
2002
            'certpass' => $certPass,
2003
            'cookies' => $this->cookies,
2004
            'debug' => $this->debug,
2005
            'extracurlopts' => $this->extracurlopts,
2006
            'keepalive' => $keepAlive,
2007
            'key' => $key,
2008
            'keypass' => $keyPass,
2009
            'no_multicall' => $this->no_multicall,
2010
            'password' => $password,
2011
            'proxy' => $proxyHost,
2012
            'proxy_authtype' => $proxyAuthType,
2013
            'proxy_pass' => $proxyPassword,
2014
            'proxyport' => $proxyPort,
2015
            'proxy_user' => $proxyUsername,
2016
            'request_charset_encoding' => $this->request_charset_encoding,
2017
            'request_compression' => $this->request_compression,
2018
            'return_type' => $this->return_type,
2019
            'sslversion' => $sslVersion,
2020
            'timeout' => $timeout,
2021
            'username' => $username,
2022
            'user_agent' => $this->user_agent,
2023
            'use_curl' => $this->use_curl,
2024
            'verifyhost' => $this->verifyhost,
2025
            'verifypeer' => $this->verifypeer,
2026
        ));
2027
    }
2028
}
2029