Passed
Push — master ( 182ddb...cf05b0 )
by Gaetano
07:26
created

Client::setCertificate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 5
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 (we could do
852
     *       as php, and allow $path to be an array of attributes...)
853 374
     */
854
    public function setCookie($name, $value = '', $path = '', $domain = '', $port = null)
855
    {
856
        $this->cookies[$name]['value'] = rawurlencode($value);
857
        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...
858
            $this->cookies[$name]['path'] = $path;
859
            $this->cookies[$name]['domain'] = $domain;
860
            $this->cookies[$name]['port'] = $port;
861
        }
862
        return $this;
863
    }
864
865
    /**
866
     * Directly set cURL options, for extra flexibility (when in cURL mode).
867
     *
868
     * It allows e.g. to bind client to a specific IP interface / address.
869
     *
870
     * @param array $options
871
     * @return $this
872
     * @deprecated use setOption
873
     */
874
    public function setCurlOptions($options)
875
    {
876
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
877
878
        $this->extracurlopts = $options;
879
        return $this;
880
    }
881
882
    /**
883
     * @param int $useCurlMode self::USE_CURL_ALWAYS, self::USE_CURL_AUTO or self::USE_CURL_NEVER
884
     * @return $this
885
     * @deprecated use setOption
886 322
     */
887
    public function setUseCurl($useCurlMode)
888
    {
889
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
890
891 322
        $this->use_curl = $useCurlMode;
892
        return $this;
893
    }
894
895 322
896
    /**
897 96
     * Set user-agent string that will be used by this client instance in http headers sent to the server.
898 96
     *
899
     * The default user agent string includes the name of this library and the version number.
900
     *
901
     * @param string $agentString
902
     * @return $this
903
     * @deprecated use setOption
904 322
     */
905 322
    public function setUserAgent($agentString)
906
    {
907
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
908
909
        $this->user_agent = $agentString;
910 322
        return $this;
911
    }
912
913
    /**
914
     * @param null|int $component allowed values: PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_PATH
915 322
     * @return string|int Notes: the path component will include query string and fragment; NULL is a valid value for port
916
     *                    (in which case the default port for http/https will be used);
917 322
     * @throws ValueErrorException on unsupported component
918
     */
919
    public function getUrl($component = null)
920
    {
921
        if (is_int($component) || ctype_digit($component)) {
922
            switch ($component) {
923
                case PHP_URL_SCHEME:
924
                    return $this->method;
925
                case PHP_URL_HOST:
926
                    return $this->server;
927
                case PHP_URL_PORT:
928
                    return $this->port;
929 322
                case  PHP_URL_PATH:
930
                    return $this->path;
931
                case '':
932 1
933 1
                default:
934 1
                    throw new ValueErrorException("Unsupported component '$component'");
935 1
            }
936 1
        }
937
938
        $url = $this->method . '://' . $this->server;
939 322
        if ($this->port == 0 || ($this->port == 80 && in_array($this->method, array('http', 'http10', 'http11', 'h2c'))) ||
940 160
            ($this->port == 443 && in_array($this->method, array('https', 'h2')))) {
941
            return $url . $this->path;
942 322
        } else {
943
            return $url . ':' . $this->port . $this->path;
944 322
        }
945
    }
946
947
    /**
948
     * Send an xml-rpc request to the server.
949
     *
950 322
     * @param Request|Request[]|string $req The Request object, or an array of requests for using multicall, or the
951
     *                                      complete xml representation of a request.
952
     *                                      When sending an array of Request objects, the client will try to make use of
953 323
     *                                      a single 'system.multicall' xml-rpc method call to forward to the server all
954
     *                                      the requests in a single HTTP round trip, unless $this->no_multicall has
955
     *                                      been previously set to TRUE (see the multicall method below), in which case
956
     *                                      many consecutive xml-rpc requests will be sent. The method will return an
957
     *                                      array of Response objects in both cases.
958 323
     *                                      The third variant allows to build by hand (or any other means) a complete
959 322
     *                                      xml-rpc request message, and send it to the server. $req should be a string
960 226
     *                                      containing the complete xml representation of the request. It is e.g. useful
961
     *                                      when, for maximal speed of execution, the request is serialized into a
962 96
     *                                      string using the native php xml-rpc functions (see http://www.php.net/xmlrpc)
963
     * @param integer $timeout deprecated. Connection timeout, in seconds, If unspecified, the timeout set with setOption
964
     *                         will be used. If that is 0, a platform specific timeout will apply.
965
     *                         This timeout value is passed to fsockopen(). It is also used for detecting server
966
     *                         timeouts during communication (i.e. if the server does not send anything to the client
967 323
     *                         for $timeout seconds, the connection will be closed).
968 302
     * @param string $method deprecated. Use the same value in the constructor instead.
969
     *                       Valid values are 'http', 'http11', 'https', 'h2' and 'h2c'. If left empty,
970
     *                       the http protocol chosen during creation of the object will be used.
971
     *                       Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c'
972 323
     *                       for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be
973 323
     *                       thus incompatible with any server/proxy not supporting http/2. This is because POST
974 64
     *                       request are not compatible with h2c upgrade.
975 32
     * @return Response|Response[] Note that the client will always return a Response object, even if the call fails
976 32
     *
977 32
     * @todo allow throwing exceptions instead of returning responses in case of failed calls and/or Fault responses
978 32
     * @todo refactor: we now support many options besides connection timeout and http version to use. Why only privilege those?
979
     */
980
    public function send($req, $timeout = 0, $method = '')
981 32
    {
982 32
        if ($method !== '' || $timeout !== 0) {
983 32
            $this->logDeprecation("Using non-default values for arguments 'method' and 'timeout' when calling method " . __METHOD__ . ' is deprecated');
984 64
        }
985
986
        // if user does not specify http protocol, use native method of this client
987
        // (i.e. method set during call to constructor)
988 259
        if ($method == '') {
989
            $method = $this->method;
990
        }
991 323
992 323
        if ($timeout == 0) {
993 64
            $timeout = $this->timeout;
994
        }
995 259
996 32
        if (is_array($req)) {
997
            // $req is an array of Requests
998
            return $this->multicall($req, $timeout, $method);
999 227
        } elseif (is_string($req)) {
1000
            $n = new static::$requestClass('');
1001
            $n->payload = $req;
1002 323
            $req = $n;
1003 323
        }
1004 322
1005
        // where req is a Request
1006
        $req->setDebug($this->debug);
1007 56
1008
        /// @todo we could be smarter about this and force usage of curl in scenarios where it is both available and
1009
        ///       needed, such as digest or ntlm auth. Do not attempt to use it for https if not present
1010
        $useCurl = ($this->use_curl == self::USE_CURL_ALWAYS) || ($this->use_curl == self::USE_CURL_AUTO &&
1011 323
                (in_array($method, array('https', 'http11', 'h2c', 'h2'))));
1012
1013 323
        // BC - we go through sendPayloadCURL/sendPayloadSocket in case some subclass reimplemented those
1014
        if ($useCurl) {
1015
            $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

1015
            $r = /** @scrutinizer ignore-deprecated */ $this->sendPayloadCURL(
Loading history...
1016
                $req,
1017 323
                $this->server,
1018
                $this->port,
1019 323
                $timeout,
1020
                $this->username,
1021 323
                $this->password,
1022
                $this->authtype,
1023
                $this->cert,
1024 323
                $this->certpass,
1025
                $this->cacert,
1026
                $this->cacertdir,
1027
                $this->proxy,
1028 323
                $this->proxyport,
1029
                $this->proxy_user,
1030
                $this->proxy_pass,
1031 66
                $this->proxy_authtype,
1032 64
                // BC
1033
                $method == 'http11' ? 'http' : $method,
1034 2
                $this->keepalive,
1035
                $this->key,
1036
                $this->keypass,
1037
                $this->sslversion
1038 323
            );
1039
        } else {
1040 323
            $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

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

1214
        /** @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...
1215
        $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

1215
        /** @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...
1216
1217
        $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr, $connectTimeout,
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

1217
        $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...
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

1217
        $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...
1218
            STREAM_CLIENT_CONNECT, $context);
1219
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1220
            if ($opts['timeout'] > 0) {
1221 23
                stream_set_timeout($fp, $opts['timeout'], 0);
1222
            }
1223
        } else {
1224
            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

1224
            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...
1225
                $err = error_get_last();
1226
                $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

1226
                /** @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...
1227
            }
1228
1229
            $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

1229
            $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...
1230
            $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::$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

1230
            $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...
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

1230
            $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...
1231
1232
            return $r;
1233
        }
1234
1235 45
        if (!fputs($fp, $op, strlen($op))) {
1236
            fclose($fp);
1237
            $this->errstr = 'Write error';
1238 45
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr);
1239 45
        }
1240 45
1241 45
        // Close socket before parsing.
1242 45
        // It should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1243 45
        $ipd = '';
1244 45
        do {
1245
            // shall we check for $data === FALSE?
1246 45
            // as per the manual, it signals an error
1247 45
            $ipd .= fread($fp, 32768);
1248
        } while (!feof($fp));
1249 45
        fclose($fp);
1250 45
1251
        return $req->parseResponse($ipd, false, $opts['return_type']);
1252
    }
1253 45
1254
    /**
1255 45
     * Contributed by Justin Miller <[email protected]>
1256
     * Requires curl to be built into PHP
1257
     * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1258
     *
1259
     * @param Request $req
1260
     * @param string $method
1261 45
     * @param string $server
1262
     * @param int $port
1263 45
     * @param string $path
1264
     * @param array $opts the keys/values match self::getOptions
1265 45
     * @return Response
1266
     *
1267 22
     * @todo the $path arg atm is ignored. What to do if it is != $this->path?
1268 22
     */
1269
    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

1269
    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...
1270
    {
1271 22
        if (!function_exists('curl_init')) {
1272 22
            $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

1272
            /** @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...
1273
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_curl'], PhpXmlRpc::$xmlrpcstr['no_curl']);
1274
        }
1275
        if ($method == 'https' || $method == 'h2') {
1276 22
            // q: what about installs where we get back a string, but curl is linked to other ssl libs than openssl?
1277 22
            if (($info = curl_version()) &&
1278 22
                ((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...
1279 22
            ) {
1280
                $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

1280
                /** @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...
1281
                return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_ssl'], PhpXmlRpc::$xmlrpcstr['no_ssl']);
1282 22
            }
1283 22
        }
1284 22
        if (($method == 'h2' && !defined('CURL_HTTP_VERSION_2_0')) ||
1285
            ($method == 'h2c' && !defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))) {
1286
            $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

1286
            /** @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...
1287
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_http2'], PhpXmlRpc::$xmlrpcstr['no_http2']);
1288 22
        }
1289 22
1290 22
        // BC - we go through prepareCurlHandle in case some subclass reimplemented it
1291
        $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

1291
        $curl = /** @scrutinizer ignore-deprecated */ $this->prepareCurlHandle($req, $server, $port, $opts['timeout'], $opts['username'], $opts['password'],
Loading history...
1292 22
            $opts['authtype'], $opts['cert'], $opts['certpass'], $opts['cacert'], $opts['cacertdir'], $opts['proxy'],
1293 22
            $opts['proxyport'], $opts['proxy_user'], $opts['proxy_pass'], $opts['proxy_authtype'], $method,
1294
            $opts['keepalive'], $opts['key'], $opts['keypass'], $opts['sslversion']);
1295
1296 22
        if (!$curl) {
1297 22
            return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] .
1298
                ': error during curl initialization. Check php error log for details');
1299
        }
1300 22
1301 22
        $result = curl_exec($curl);
1302
1303
        if ($opts['debug'] > 1) {
1304
            $message = "---CURL INFO---\n";
1305
            foreach (curl_getinfo($curl) as $name => $val) {
1306
                if (is_array($val)) {
1307 22
                    $val = implode("\n", $val);
1308
                }
1309
                $message .= $name . ': ' . $val . "\n";
1310
            }
1311 24
            $message .= '---END---';
1312 24
            $this->getLogger()->debug($message);
1313
        }
1314
1315 24
        if (!$result) {
1316 24
            /// @todo we should use a better check here - what if we get back '' or '0'?
1317
1318
            $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

1318
            /** @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...
1319
            $resp = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] .
1320 24
                ': ' . curl_error($curl));
1321 24
            curl_close($curl);
1322 24
            if ($opts['keepalive']) {
1323 24
                $this->xmlrpc_curl_handle = null;
1324 24
            }
1325
        } else {
1326
            if (!$opts['keepalive']) {
1327
                curl_close($curl);
1328 24
            }
1329 24
            $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

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

1730
            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...
1731
                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1732
                    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 1642. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1733
                    $result->httpResponse());
1734
            }
1735
            $numRets = $rets->count();
1736
            if ($numRets != count($reqs)) {
1737
                // wrong number of return values.
1738
                return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1739
                    PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'xmlrpcvals',
1740
                    $result->httpResponse());
1741
            }
1742
1743
            foreach ($rets as $i => $val) {
0 ignored issues
show
Bug introduced by
The expression $rets of type integer is not traversable.
Loading history...
1744
                switch ($val->kindOf()) {
1745
                    case 'array':
1746
                        if ($val->count() != 1) {
1747
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1748
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
1749
                                'phpvals', $result->httpResponse());
1750
                        }
1751
                        // Normal return value
1752
                        $response[] = new Response($val[0], 0, '', 'xmlrpcvals', $result->httpResponse());
1753
                        break;
1754
                    case 'struct':
1755
                        if ($val->count() != 2) {
1756
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1757
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items",
1758
                                'phpvals', $result->httpResponse());
1759
                        }
1760
                        /** @var Value $code */
1761
                        $code = $val['faultCode'];
1762
                        if ($code->kindOf() != 'scalar' || $code->scalarTyp() != 'int') {
1763
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1764
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
1765
                                'xmlrpcvals', $result->httpResponse());
1766
                        }
1767
                        /** @var Value $str */
1768
                        $str = $val['faultString'];
1769
                        if ($str->kindOf() != 'scalar' || $str->scalarTyp() != 'string') {
1770
                            return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1771
                                PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode",
1772
                                'xmlrpcvals', $result->httpResponse());
1773
                        }
1774
                        $response[] = new Response(0, $code->scalarVal(), $str->scalarVal(), 'xmlrpcvals', $result->httpResponse());
1775
                        break;
1776
                    default:
1777
                        return new Response(0, PhpXmlRpc::$xmlrpcerr['multicall_error'],
1778
                            PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct",
1779
                            'xmlrpcvals', $result->httpResponse());
1780
                }
1781
            }
1782
        }
1783
1784
        return $response;
1785
    }
1786
1787
    // *** BC layer ***
1788
1789
    /**
1790
     * @deprecated
1791
     *
1792
     * @param Request $req
1793
     * @param string $server
1794
     * @param int $port
1795
     * @param int $timeout
1796
     * @param string $username
1797
     * @param string $password
1798
     * @param int $authType
1799
     * @param string $proxyHost
1800
     * @param int $proxyPort
1801
     * @param string $proxyUsername
1802
     * @param string $proxyPassword
1803
     * @param int $proxyAuthType
1804
     * @param string $method
1805
     * @return Response
1806
     */
1807
    protected function sendPayloadHTTP10($req, $server, $port, $timeout = 0, $username = '', $password = '',
1808
        $authType = 1, $proxyHost = '', $proxyPort = 0, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1,
1809
        $method = 'http')
1810
    {
1811
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
1812
1813
        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

1813
        return /** @scrutinizer ignore-deprecated */ $this->sendPayloadSocket($req, $server, $port, $timeout, $username, $password, $authType, null, null,
Loading history...
1814
            null, null, $proxyHost, $proxyPort, $proxyUsername, $proxyPassword, $proxyAuthType, $method);
1815
    }
1816
1817
    /**
1818
     * @deprecated
1819
     *
1820
     * @param Request $req
1821
     * @param string $server
1822
     * @param int $port
1823
     * @param int $timeout
1824
     * @param string $username
1825
     * @param string $password
1826
     * @param int $authType
1827
     * @param string $cert
1828
     * @param string $certPass
1829
     * @param string $caCert
1830
     * @param string $caCertDir
1831
     * @param string $proxyHost
1832
     * @param int $proxyPort
1833
     * @param string $proxyUsername
1834
     * @param string $proxyPassword
1835
     * @param int $proxyAuthType
1836
     * @param bool $keepAlive
1837
     * @param string $key
1838
     * @param string $keyPass
1839
     * @param int $sslVersion
1840
     * @return Response
1841
     */
1842
    protected function sendPayloadHTTPS($req, $server, $port, $timeout = 0, $username = '', $password = '',
1843
        $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0,
1844
        $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $keepAlive = false, $key = '', $keyPass = '',
1845
        $sslVersion = 0)
1846
    {
1847
        $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated');
1848
1849
        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

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