Differences Between: [Versions 401 and 403]
1 <?php 2 3 namespace PhpXmlRpc; 4 5 use PhpXmlRpc\Exception\ValueErrorException; 6 use PhpXmlRpc\Helper\XMLParser; 7 use PhpXmlRpc\Traits\CharsetEncoderAware; 8 use PhpXmlRpc\Traits\DeprecationLogger; 9 10 /** 11 * Used to represent a client of an XML-RPC server. 12 * 13 * @property int $errno deprecated - public access left in purely for BC. 14 * @property string $errstr deprecated - public access left in purely for BC. 15 * @property string $method deprecated - public access left in purely for BC. Access via getUrl()/__construct() 16 * @property string $server deprecated - public access left in purely for BC. Access via getUrl()/__construct() 17 * @property int $port deprecated - public access left in purely for BC. Access via getUrl()/__construct() 18 * @property string $path deprecated - public access left in purely for BC. Access via getUrl()/__construct() 19 */ 20 class Client 21 { 22 use DeprecationLogger; 23 //use CharsetEncoderAware; 24 25 const USE_CURL_NEVER = 0; 26 const USE_CURL_ALWAYS = 1; 27 const USE_CURL_AUTO = 2; 28 29 const OPT_ACCEPTED_CHARSET_ENCODINGS = 'accepted_charset_encodings'; 30 const OPT_ACCEPTED_COMPRESSION = 'accepted_compression'; 31 const OPT_AUTH_TYPE = 'authtype'; 32 const OPT_CA_CERT = 'cacert'; 33 const OPT_CA_CERT_DIR = 'cacertdir'; 34 const OPT_CERT = 'cert'; 35 const OPT_CERT_PASS = 'certpass'; 36 const OPT_COOKIES = 'cookies'; 37 const OPT_DEBUG = 'debug'; 38 const OPT_EXTRA_CURL_OPTS = 'extracurlopts'; 39 const OPT_EXTRA_SOCKET_OPTS = 'extrasockopts'; 40 const OPT_KEEPALIVE = 'keepalive'; 41 const OPT_KEY = 'key'; 42 const OPT_KEY_PASS = 'keypass'; 43 const OPT_NO_MULTICALL = 'no_multicall'; 44 const OPT_PASSWORD = 'password'; 45 const OPT_PROXY = 'proxy'; 46 const OPT_PROXY_AUTH_TYPE = 'proxy_authtype'; 47 const OPT_PROXY_PASS = 'proxy_pass'; 48 const OPT_PROXY_PORT = 'proxyport'; 49 const OPT_PROXY_USER = 'proxy_user'; 50 const OPT_REQUEST_CHARSET_ENCODING = 'request_charset_encoding'; 51 const OPT_REQUEST_COMPRESSION = 'request_compression'; 52 const OPT_RETURN_TYPE = 'return_type'; 53 const OPT_SSL_VERSION = 'sslversion'; 54 const OPT_TIMEOUT = 'timeout'; 55 const OPT_USERNAME = 'username'; 56 const OPT_USER_AGENT = 'user_agent'; 57 const OPT_USE_CURL = 'use_curl'; 58 const OPT_VERIFY_HOST = 'verifyhost'; 59 const OPT_VERIFY_PEER = 'verifypeer'; 60 61 /** @var string */ 62 protected static $requestClass = '\\PhpXmlRpc\\Request'; 63 /** @var string */ 64 protected static $responseClass = '\\PhpXmlRpc\\Response'; 65 66 /** 67 * @var int 68 * @deprecated will be removed in the future 69 */ 70 protected $errno; 71 /** 72 * @var string 73 * @deprecated will be removed in the future 74 */ 75 protected $errstr; 76 77 /// @todo: do all the ones below need to be public? 78 79 /** 80 * @var string 81 */ 82 protected $method = 'http'; 83 /** 84 * @var string 85 */ 86 protected $server; 87 /** 88 * @var int 89 */ 90 protected $port = 0; 91 /** 92 * @var string 93 */ 94 protected $path; 95 96 /** 97 * @var int 98 */ 99 protected $debug = 0; 100 /** 101 * @var string 102 */ 103 protected $username = ''; 104 /** 105 * @var string 106 */ 107 protected $password = ''; 108 /** 109 * @var int 110 */ 111 protected $authtype = 1; 112 /** 113 * @var string 114 */ 115 protected $cert = ''; 116 /** 117 * @var string 118 */ 119 protected $certpass = ''; 120 /** 121 * @var string 122 */ 123 protected $cacert = ''; 124 /** 125 * @var string 126 */ 127 protected $cacertdir = ''; 128 /** 129 * @var string 130 */ 131 protected $key = ''; 132 /** 133 * @var string 134 */ 135 protected $keypass = ''; 136 /** 137 * @var bool 138 */ 139 protected $verifypeer = true; 140 /** 141 * @var int 142 */ 143 protected $verifyhost = 2; 144 /** 145 * @var int 146 */ 147 protected $sslversion = 0; // corresponds to CURL_SSLVERSION_DEFAULT. Other CURL_SSLVERSION_ values are supported 148 /** 149 * @var string 150 */ 151 protected $proxy = ''; 152 /** 153 * @var int 154 */ 155 protected $proxyport = 0; 156 /** 157 * @var string 158 */ 159 protected $proxy_user = ''; 160 /** 161 * @var string 162 */ 163 protected $proxy_pass = ''; 164 /** 165 * @var int 166 */ 167 protected $proxy_authtype = 1; 168 /** 169 * @var array 170 */ 171 protected $cookies = array(); 172 /** 173 * @var array 174 */ 175 protected $extrasockopts = array(); 176 /** 177 * @var array 178 */ 179 protected $extracurlopts = array(); 180 /** 181 * @var int 182 */ 183 protected $timeout = 0; 184 /** 185 * @var int 186 */ 187 protected $use_curl = self::USE_CURL_AUTO; 188 /** 189 * @var bool 190 * 191 * This determines whether the multicall() method will try to take advantage of the system.multicall xml-rpc method 192 * to dispatch to the server an array of requests in a single http roundtrip or simply execute many consecutive http 193 * calls. Defaults to FALSE, but it will be enabled automatically on the first failure of execution of 194 * system.multicall. 195 */ 196 protected $no_multicall = false; 197 /** 198 * @var array 199 * 200 * List of http compression methods accepted by the client for responses. 201 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib. 202 * 203 * 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 204 * decide the compression methods it supports. You might check for the presence of 'zlib' in the output of 205 * curl_version() to determine whether compression is supported or not 206 */ 207 protected $accepted_compression = array(); 208 /** 209 * @var string|null 210 * 211 * Name of compression scheme to be used for sending requests. 212 * Either null, 'gzip' or 'deflate'. 213 */ 214 protected $request_compression = ''; 215 /** 216 * @var bool 217 * 218 * Whether to use persistent connections for http 1.1 and https. Value set at constructor time. 219 */ 220 protected $keepalive = false; 221 /** 222 * @var string[] 223 * 224 * Charset encodings that can be decoded without problems by the client. Value set at constructor time 225 */ 226 protected $accepted_charset_encodings = array(); 227 /** 228 * @var string 229 * 230 * The charset encoding that will be used for serializing request sent by the client. 231 * It defaults to NULL, which means using US-ASCII and encoding all characters outside the ASCII printable range 232 * using their xml character entity representation (this has the benefit that line end characters will not be mangled 233 * in the transfer, a CR-LF will be preserved as well as a singe LF). 234 * Valid values are 'US-ASCII', 'UTF-8' and 'ISO-8859-1'. 235 * For the fastest mode of operation, set your both your app internal encoding and this to UTF-8. 236 */ 237 protected $request_charset_encoding = ''; 238 /** 239 * @var string 240 * 241 * Decides the content of Response objects returned by calls to send() and multicall(). 242 * Valid values are 'xmlrpcvals', 'phpvals' or 'xml'. 243 * 244 * Determines whether the value returned inside a Response object as results of calls to the send() and multicall() 245 * methods will be a Value object, a plain php value or a raw xml string. 246 * Allowed values are 'xmlrpcvals' (the default), 'phpvals' and 'xml'. 247 * To allow the user to differentiate between a correct and a faulty response, fault responses will be returned as 248 * Response objects in any case. 249 * Note that the 'phpvals' setting will yield faster execution times, but some of the information from the original 250 * response will be lost. It will be e.g. impossible to tell whether a particular php string value was sent by the 251 * server as an xml-rpc string or base64 value. 252 */ 253 protected $return_type = XMLParser::RETURN_XMLRPCVALS; 254 /** 255 * @var string 256 * 257 * Sent to servers in http headers. Value set at constructor time. 258 */ 259 protected $user_agent; 260 261 /** 262 * CURL handle: used for keep-alive 263 * @internal 264 */ 265 public $xmlrpc_curl_handle = null; 266 267 /** 268 * @var array 269 */ 270 protected static $options = array( 271 self::OPT_ACCEPTED_CHARSET_ENCODINGS, 272 self::OPT_ACCEPTED_COMPRESSION, 273 self::OPT_AUTH_TYPE, 274 self::OPT_CA_CERT, 275 self::OPT_CA_CERT_DIR, 276 self::OPT_CERT, 277 self::OPT_CERT_PASS, 278 self::OPT_COOKIES, 279 self::OPT_DEBUG, 280 self::OPT_EXTRA_CURL_OPTS, 281 self::OPT_EXTRA_SOCKET_OPTS, 282 self::OPT_KEEPALIVE, 283 self::OPT_KEY, 284 self::OPT_KEY_PASS, 285 self::OPT_NO_MULTICALL, 286 self::OPT_PASSWORD, 287 self::OPT_PROXY, 288 self::OPT_PROXY_AUTH_TYPE, 289 self::OPT_PROXY_PASS, 290 self::OPT_PROXY_USER, 291 self::OPT_PROXY_PORT, 292 self::OPT_REQUEST_CHARSET_ENCODING, 293 self::OPT_REQUEST_COMPRESSION, 294 self::OPT_RETURN_TYPE, 295 self::OPT_SSL_VERSION, 296 self::OPT_TIMEOUT, 297 self::OPT_USE_CURL, 298 self::OPT_USER_AGENT, 299 self::OPT_USERNAME, 300 self::OPT_VERIFY_HOST, 301 self::OPT_VERIFY_PEER, 302 ); 303 304 /** 305 * @param string $path either the PATH part of the xml-rpc server URL, or complete server URL (in which case you 306 * should use an empty string for all other parameters) 307 * e.g. /xmlrpc/server.php 308 * e.g. http://phpxmlrpc.sourceforge.net/server.php 309 * e.g. https://james:bond@secret.service.com:444/xmlrpcserver?agent=007 310 * e.g. h2://fast-and-secure-services.org/endpoint 311 * @param string $server the server name / ip address 312 * @param integer $port the port the server is listening on, when omitted defaults to 80 or 443 depending on 313 * protocol used 314 * @param string $method the http protocol variant: defaults to 'http'; 'https', 'http11', 'h2' and 'h2c' can 315 * be used if CURL is installed. The value set here can be overridden in any call to $this->send(). 316 * Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c' 317 * for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be 318 * thus incompatible with any server/proxy not supporting http/2. This is because POST 319 * request are not compatible with h2c upgrade. 320 */ 321 public function __construct($path, $server = '', $port = '', $method = '') 322 { 323 // allow user to specify all params in $path 324 if ($server == '' && $port == '' && $method == '') { 325 $parts = parse_url($path); 326 $server = $parts['host']; 327 $path = isset($parts['path']) ? $parts['path'] : ''; 328 if (isset($parts['query'])) { 329 $path .= '?' . $parts['query']; 330 } 331 if (isset($parts['fragment'])) { 332 $path .= '#' . $parts['fragment']; 333 } 334 if (isset($parts['port'])) { 335 $port = $parts['port']; 336 } 337 if (isset($parts['scheme'])) { 338 $method = $parts['scheme']; 339 } 340 if (isset($parts['user'])) { 341 $this->username = $parts['user']; 342 } 343 if (isset($parts['pass'])) { 344 $this->password = $parts['pass']; 345 } 346 } 347 if ($path == '' || $path[0] != '/') { 348 $this->path = '/' . $path; 349 } else { 350 $this->path = $path; 351 } 352 $this->server = $server; 353 if ($port != '') { 354 $this->port = $port; 355 } 356 if ($method != '') { 357 $this->method = $method; 358 } 359 360 // if ZLIB is enabled, let the client by default accept compressed responses 361 if (function_exists('gzinflate') || ( 362 function_exists('curl_version') && (($info = curl_version()) && 363 ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version']))) 364 ) 365 ) { 366 $this->accepted_compression = array('gzip', 'deflate'); 367 } 368 369 // keepalives: enabled by default 370 $this->keepalive = true; 371 372 // by default the xml parser can support these 3 charset encodings 373 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII'); 374 375 // NB: this is disabled to avoid making all the requests sent huge... mbstring supports more than 80 charsets! 376 //$ch = $this->getCharsetEncoder(); 377 //$this->accepted_charset_encodings = $ch->knownCharsets(); 378 379 // initialize user_agent string 380 $this->user_agent = PhpXmlRpc::$xmlrpcName . ' ' . PhpXmlRpc::$xmlrpcVersion; 381 } 382 383 /** 384 * @param string $name see all the OPT_ constants 385 * @param mixed $value 386 * @return $this 387 * @throws ValueErrorException on unsupported option 388 */ 389 public function setOption($name, $value) 390 { 391 if (in_array($name, static::$options)) { 392 $this->$name = $value; 393 return $this; 394 } 395 396 throw new ValueErrorException("Unsupported option '$name'"); 397 } 398 399 /** 400 * @param string $name see all the OPT_ constants 401 * @return mixed 402 * @throws ValueErrorException on unsupported option 403 */ 404 public function getOption($name) 405 { 406 if (in_array($name, static::$options)) { 407 return $this->$name; 408 } 409 410 throw new ValueErrorException("Unsupported option '$name'"); 411 } 412 413 /** 414 * Returns the complete list of Client options, with their value. 415 * @return array 416 */ 417 public function getOptions() 418 { 419 $values = array(); 420 foreach (static::$options as $opt) { 421 $values[$opt] = $this->getOption($opt); 422 } 423 return $values; 424 } 425 426 /** 427 * @param array $options key: any valid option (see all the OPT_ constants) 428 * @return $this 429 * @throws ValueErrorException on unsupported option 430 */ 431 public function setOptions($options) 432 { 433 foreach ($options as $name => $value) { 434 $this->setOption($name, $value); 435 } 436 437 return $this; 438 } 439 440 /** 441 * Enable/disable the echoing to screen of the xml-rpc responses received. The default is not to output anything. 442 * 443 * The debugging information at level 1 includes the raw data returned from the XML-RPC server it was querying 444 * (including bot HTTP headers and the full XML payload), and the PHP value the client attempts to create to 445 * represent the value returned by the server. 446 * At level 2, the complete payload of the xml-rpc request is also printed, before being sent to the server. 447 * At level -1, the Response objects returned by send() calls will not carry information about the http response's 448 * cookies, headers and body, which might save some memory 449 * 450 * This option can be very useful when debugging servers as it allows you to see exactly what the client sends and 451 * the server returns. Never leave it enabled for production! 452 * 453 * @param integer $level values -1, 0, 1 and 2 are supported 454 * @return $this 455 */ 456 public function setDebug($level) 457 { 458 $this->debug = $level; 459 return $this; 460 } 461 462 /** 463 * Sets the username and password for authorizing the client to the server. 464 * 465 * With the default (HTTP) transport, this information is used for HTTP Basic authorization. 466 * Note that username and password can also be set using the class constructor. 467 * With HTTP 1.1 and HTTPS transport, NTLM and Digest authentication protocols are also supported. To enable them use 468 * the constants CURLAUTH_DIGEST and CURLAUTH_NTLM as values for the auth type parameter. 469 * 470 * @param string $user username 471 * @param string $password password 472 * @param integer $authType auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC 473 * (basic auth). Note that auth types NTLM and Digest will only work if the Curl php 474 * extension is enabled. 475 * @return $this 476 */ 477 public function setCredentials($user, $password, $authType = 1) 478 { 479 $this->username = $user; 480 $this->password = $password; 481 $this->authtype = $authType; 482 return $this; 483 } 484 485 /** 486 * Set the optional certificate and passphrase used in SSL-enabled communication with a remote server. 487 * 488 * Note: to retrieve information about the client certificate on the server side, you will need to look into the 489 * environment variables which are set up by the webserver. Different webservers will typically set up different 490 * variables. 491 * 492 * @param string $cert the name of a file containing a PEM formatted certificate 493 * @param string $certPass the password required to use it 494 * @return $this 495 */ 496 public function setCertificate($cert, $certPass = '') 497 { 498 $this->cert = $cert; 499 $this->certpass = $certPass; 500 return $this; 501 } 502 503 /** 504 * Add a CA certificate to verify server with in SSL-enabled communication when SetSSLVerifypeer has been set to TRUE. 505 * 506 * See the php manual page about CURLOPT_CAINFO for more details. 507 * 508 * @param string $caCert certificate file name (or dir holding certificates) 509 * @param bool $isDir set to true to indicate cacert is a dir. defaults to false 510 * @return $this 511 */ 512 public function setCaCertificate($caCert, $isDir = false) 513 { 514 if ($isDir) { 515 $this->cacertdir = $caCert; 516 } else { 517 $this->cacert = $caCert; 518 } 519 return $this; 520 } 521 522 /** 523 * Set attributes for SSL communication: private SSL key. 524 * 525 * NB: does not work in older php/curl installs. 526 * Thanks to Daniel Convissor. 527 * 528 * @param string $key The name of a file containing a private SSL key 529 * @param string $keyPass The secret password needed to use the private SSL key 530 * @return $this 531 */ 532 public function setKey($key, $keyPass) 533 { 534 $this->key = $key; 535 $this->keypass = $keyPass; 536 return $this; 537 } 538 539 /** 540 * Set attributes for SSL communication: verify the remote host's SSL certificate, and cause the connection to fail 541 * if the cert verification fails. 542 * 543 * By default, verification is enabled. 544 * To specify custom SSL certificates to validate the server with, use the setCaCertificate method. 545 * 546 * @param bool $i enable/disable verification of peer certificate 547 * @return $this 548 * @deprecated use setOption 549 */ 550 public function setSSLVerifyPeer($i) 551 { 552 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 553 554 $this->verifypeer = $i; 555 return $this; 556 } 557 558 /** 559 * Set attributes for SSL communication: verify the remote host's SSL certificate's common name (CN). 560 * 561 * Note that support for value 1 has been removed in cURL 7.28.1 562 * 563 * @param int $i Set to 1 to only the existence of a CN, not that it matches 564 * @return $this 565 * @deprecated use setOption 566 */ 567 public function setSSLVerifyHost($i) 568 { 569 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 570 571 $this->verifyhost = $i; 572 return $this; 573 } 574 575 /** 576 * Set attributes for SSL communication: SSL version to use. Best left at 0 (default value): let cURL decide 577 * 578 * @param int $i see CURL_SSLVERSION_ constants 579 * @return $this 580 * @deprecated use setOption 581 */ 582 public function setSSLVersion($i) 583 { 584 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 585 586 $this->sslversion = $i; 587 return $this; 588 } 589 590 /** 591 * Set proxy info. 592 * 593 * NB: CURL versions before 7.11.10 cannot use a proxy to communicate with https servers. 594 * 595 * @param string $proxyHost 596 * @param string $proxyPort Defaults to 8080 for HTTP and 443 for HTTPS 597 * @param string $proxyUsername Leave blank if proxy has public access 598 * @param string $proxyPassword Leave blank if proxy has public access 599 * @param int $proxyAuthType defaults to CURLAUTH_BASIC (Basic authentication protocol); set to constant CURLAUTH_NTLM 600 * to use NTLM auth with proxy (has effect only when the client uses the HTTP 1.1 protocol) 601 * @return $this 602 */ 603 public function setProxy($proxyHost, $proxyPort, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1) 604 { 605 $this->proxy = $proxyHost; 606 $this->proxyport = $proxyPort; 607 $this->proxy_user = $proxyUsername; 608 $this->proxy_pass = $proxyPassword; 609 $this->proxy_authtype = $proxyAuthType; 610 return $this; 611 } 612 613 /** 614 * Enables/disables reception of compressed xml-rpc responses. 615 * 616 * This requires the "zlib" extension to be enabled in your php install. If it is, by default xmlrpc_client 617 * instances will enable reception of compressed content. 618 * Note that enabling reception of compressed responses merely adds some standard http headers to xml-rpc requests. 619 * It is up to the xml-rpc server to return compressed responses when receiving such requests. 620 * 621 * @param string $compMethod either 'gzip', 'deflate', 'any' or '' 622 * @return $this 623 */ 624 public function setAcceptedCompression($compMethod) 625 { 626 if ($compMethod == 'any') { 627 $this->accepted_compression = array('gzip', 'deflate'); 628 } elseif ($compMethod == false) { 629 $this->accepted_compression = array(); 630 } else { 631 $this->accepted_compression = array($compMethod); 632 } 633 return $this; 634 } 635 636 /** 637 * Enables/disables http compression of xml-rpc request. 638 * 639 * This requires the "zlib" extension to be enabled in your php install. 640 * Take care when sending compressed requests: servers might not support them (and automatic fallback to 641 * uncompressed requests is not yet implemented). 642 * 643 * @param string $compMethod either 'gzip', 'deflate' or '' 644 * @return $this 645 * @deprecated use setOption 646 */ 647 public function setRequestCompression($compMethod) 648 { 649 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 650 651 $this->request_compression = $compMethod; 652 return $this; 653 } 654 655 /** 656 * Adds a cookie to list of cookies that will be sent to server with every further request (useful e.g. for keeping 657 * session info outside the xml-rpc payload). 658 * 659 * NB: by default all cookies set via this method are sent to the server, regardless of path/domain/port. Taking 660 * advantage of those values is left to the single developer. 661 * 662 * @param string $name nb: will not be escaped in the request's http headers. Take care not to use CTL chars or 663 * separators! 664 * @param string $value 665 * @param string $path 666 * @param string $domain 667 * @param int $port do not use! Cookies are not separated by port 668 * @return $this 669 * 670 * @todo check correctness of urlencoding cookie value (copied from php way of doing it, but php is generally sending 671 * response not requests. We do the opposite...) 672 * @todo strip invalid chars from cookie name? As per RFC6265, we should follow RFC2616, Section 2.2 673 * @todo drop/rename $port parameter. Cookies are not isolated by port! 674 * @todo feature-creep allow storing 'expires', 'secure', 'httponly' and 'samesite' cookie attributes (we could do 675 * as php, and allow $path to be an array of attributes...) 676 */ 677 public function setCookie($name, $value = '', $path = '', $domain = '', $port = null) 678 { 679 $this->cookies[$name]['value'] = rawurlencode($value); 680 if ($path || $domain || $port) { 681 $this->cookies[$name]['path'] = $path; 682 $this->cookies[$name]['domain'] = $domain; 683 $this->cookies[$name]['port'] = $port; 684 } 685 return $this; 686 } 687 688 /** 689 * Directly set cURL options, for extra flexibility (when in cURL mode). 690 * 691 * It allows e.g. to bind client to a specific IP interface / address. 692 * 693 * @param array $options 694 * @return $this 695 * @deprecated use setOption 696 */ 697 public function setCurlOptions($options) 698 { 699 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 700 701 $this->extracurlopts = $options; 702 return $this; 703 } 704 705 /** 706 * @param int $useCurlMode self::USE_CURL_ALWAYS, self::USE_CURL_AUTO or self::USE_CURL_NEVER 707 * @return $this 708 * @deprecated use setOption 709 */ 710 public function setUseCurl($useCurlMode) 711 { 712 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 713 714 $this->use_curl = $useCurlMode; 715 return $this; 716 } 717 718 719 /** 720 * Set user-agent string that will be used by this client instance in http headers sent to the server. 721 * 722 * The default user agent string includes the name of this library and the version number. 723 * 724 * @param string $agentString 725 * @return $this 726 * @deprecated use setOption 727 */ 728 public function setUserAgent($agentString) 729 { 730 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 731 732 $this->user_agent = $agentString; 733 return $this; 734 } 735 736 /** 737 * @param null|int $component allowed values: PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_PATH 738 * @return string|int Notes: the path component will include query string and fragment; NULL is a valid value for port 739 * (in which case the default port for http/https will be used); 740 * @throws ValueErrorException on unsupported component 741 */ 742 public function getUrl($component = null) 743 { 744 if (is_int($component) || ctype_digit($component)) { 745 switch ($component) { 746 case PHP_URL_SCHEME: 747 return $this->method; 748 case PHP_URL_HOST: 749 return $this->server; 750 case PHP_URL_PORT: 751 return $this->port; 752 case PHP_URL_PATH: 753 return $this->path; 754 case '': 755 756 default: 757 throw new ValueErrorException("Unsupported component '$component'"); 758 } 759 } 760 761 $url = $this->method . '://' . $this->server; 762 if ($this->port == 0 || ($this->port == 80 && in_array($this->method, array('http', 'http10', 'http11', 'h2c'))) || 763 ($this->port == 443 && in_array($this->method, array('https', 'h2')))) { 764 return $url . $this->path; 765 } else { 766 return $url . ':' . $this->port . $this->path; 767 } 768 } 769 770 /** 771 * Send an xml-rpc request to the server. 772 * 773 * @param Request|Request[]|string $req The Request object, or an array of requests for using multicall, or the 774 * complete xml representation of a request. 775 * When sending an array of Request objects, the client will try to make use of 776 * a single 'system.multicall' xml-rpc method call to forward to the server all 777 * the requests in a single HTTP round trip, unless $this->no_multicall has 778 * been previously set to TRUE (see the multicall method below), in which case 779 * many consecutive xml-rpc requests will be sent. The method will return an 780 * array of Response objects in both cases. 781 * The third variant allows to build by hand (or any other means) a complete 782 * xml-rpc request message, and send it to the server. $req should be a string 783 * containing the complete xml representation of the request. It is e.g. useful 784 * when, for maximal speed of execution, the request is serialized into a 785 * string using the native php xml-rpc functions (see http://www.php.net/xmlrpc) 786 * @param integer $timeout deprecated. Connection timeout, in seconds, If unspecified, the timeout set with setOption 787 * will be used. If that is 0, a platform specific timeout will apply. 788 * This timeout value is passed to fsockopen(). It is also used for detecting server 789 * timeouts during communication (i.e. if the server does not send anything to the client 790 * for $timeout seconds, the connection will be closed). 791 * @param string $method deprecated. Use the same value in the constructor instead. 792 * Valid values are 'http', 'http11', 'https', 'h2' and 'h2c'. If left empty, 793 * the http protocol chosen during creation of the object will be used. 794 * Use 'h2' to make the lib attempt to use http/2 over a secure connection, and 'h2c' 795 * for http/2 without tls. Note that 'h2c' will not use the h2c 'upgrade' method, and be 796 * thus incompatible with any server/proxy not supporting http/2. This is because POST 797 * request are not compatible with h2c upgrade. 798 * @return Response|Response[] Note that the client will always return a Response object, even if the call fails 799 * 800 * @todo allow throwing exceptions instead of returning responses in case of failed calls and/or Fault responses 801 * @todo refactor: we now support many options besides connection timeout and http version to use. Why only privilege those? 802 */ 803 public function send($req, $timeout = 0, $method = '') 804 { 805 if ($method !== '' || $timeout !== 0) { 806 $this->logDeprecation("Using non-default values for arguments 'method' and 'timeout' when calling method " . __METHOD__ . ' is deprecated'); 807 } 808 809 // if user does not specify http protocol, use native method of this client 810 // (i.e. method set during call to constructor) 811 if ($method == '') { 812 $method = $this->method; 813 } 814 815 if ($timeout == 0) { 816 $timeout = $this->timeout; 817 } 818 819 if (is_array($req)) { 820 // $req is an array of Requests 821 /// @todo switch to the new syntax for multicall 822 return $this->multicall($req, $timeout, $method); 823 } elseif (is_string($req)) { 824 $n = new static::$requestClass(''); 825 $n->setPayload($req); 826 $req = $n; 827 } 828 829 // where req is a Request 830 $req->setDebug($this->debug); 831 832 /// @todo we could be smarter about this and not force usage of curl for https if not present as well as use the 833 /// presence of curl_extra_opts or socket_extra_opts as a hint 834 $useCurl = ($this->use_curl == self::USE_CURL_ALWAYS) || ($this->use_curl == self::USE_CURL_AUTO && ( 835 in_array($method, array('https', 'http11', 'h2c', 'h2')) || 836 ($this->username != '' && $this->authtype != 1) || 837 ($this->proxy != '' && $this->proxy_user != '' && $this->proxy_authtype != 1) 838 )); 839 840 // BC - we go through sendPayloadCURL/sendPayloadSocket in case some subclass reimplemented those 841 if ($useCurl) { 842 $r = $this->sendPayloadCURL( 843 $req, 844 $this->server, 845 $this->port, 846 $timeout, 847 $this->username, 848 $this->password, 849 $this->authtype, 850 $this->cert, 851 $this->certpass, 852 $this->cacert, 853 $this->cacertdir, 854 $this->proxy, 855 $this->proxyport, 856 $this->proxy_user, 857 $this->proxy_pass, 858 $this->proxy_authtype, 859 // BC 860 $method == 'http11' ? 'http' : $method, 861 $this->keepalive, 862 $this->key, 863 $this->keypass, 864 $this->sslversion 865 ); 866 } else { 867 $r = $this->sendPayloadSocket( 868 $req, 869 $this->server, 870 $this->port, 871 $timeout, 872 $this->username, 873 $this->password, 874 $this->authtype, 875 $this->cert, 876 $this->certpass, 877 $this->cacert, 878 $this->cacertdir, 879 $this->proxy, 880 $this->proxyport, 881 $this->proxy_user, 882 $this->proxy_pass, 883 $this->proxy_authtype, 884 $method, 885 $this->key, 886 $this->keypass, 887 $this->sslversion 888 ); 889 } 890 891 return $r; 892 } 893 894 /** 895 * @param Request $req 896 * @param string $method 897 * @param string $server 898 * @param int $port 899 * @param string $path 900 * @param array $opts 901 * @return Response 902 */ 903 protected function sendViaSocket($req, $method, $server, $port, $path, $opts) 904 { 905 /// @todo log a warning if passed an unsupported method 906 907 // Only create the payload if it was not created previously 908 /// @todo what if the request's payload was created with a different encoding? 909 /// Also, if we do not call serialize(), the request will not set its content-type to have the charset declared 910 $payload = $req->getPayload(); 911 if (empty($payload)) { 912 $payload = $req->serialize($opts['request_charset_encoding']); 913 } 914 915 // Deflate request body and set appropriate request headers 916 $encodingHdr = ''; 917 if ($opts['request_compression'] == 'gzip' || $opts['request_compression'] == 'deflate') { 918 if ($opts['request_compression'] == 'gzip' && function_exists('gzencode')) { 919 $a = @gzencode($payload); 920 if ($a) { 921 $payload = $a; 922 $encodingHdr = "Content-Encoding: gzip\r\n"; 923 } else { 924 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzencode failure in compressing request'); 925 } 926 } else if (function_exists('gzcompress')) { 927 $a = @gzcompress($payload); 928 if ($a) { 929 $payload = $a; 930 $encodingHdr = "Content-Encoding: deflate\r\n"; 931 } else { 932 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzcompress failure in compressing request'); 933 } 934 } else { 935 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported by this PHP install'); 936 } 937 } else { 938 if ($opts['request_compression'] != '') { 939 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported'); 940 } 941 } 942 943 // thanks to Grant Rauscher 944 $credentials = ''; 945 if ($opts['username'] != '') { 946 if ($opts['authtype'] != 1) { 947 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported with HTTP 1.0'); 948 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], 949 PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': only Basic auth is supported with HTTP 1.0'); 950 } 951 $credentials = 'Authorization: Basic ' . base64_encode($opts['username'] . ':' . $opts['password']) . "\r\n"; 952 } 953 954 $acceptedEncoding = ''; 955 if (is_array($opts['accepted_compression']) && count($opts['accepted_compression'])) { 956 $acceptedEncoding = 'Accept-Encoding: ' . implode(', ', $opts['accepted_compression']) . "\r\n"; 957 } 958 959 if ($port == 0) { 960 $port = ($method === 'https') ? 443 : 80; 961 } 962 963 $proxyCredentials = ''; 964 if ($opts['proxy']) { 965 if ($opts['proxyport'] == 0) { 966 $opts['proxyport'] = 8080; 967 } 968 $connectServer = $opts['proxy']; 969 $connectPort = $opts['proxyport']; 970 $transport = 'tcp'; 971 /// @todo check: should we not use https in some cases? 972 $uri = 'http://' . $server . ':' . $port . $path; 973 if ($opts['proxy_user'] != '') { 974 if ($opts['proxy_authtype'] != 1) { 975 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported with HTTP 1.0'); 976 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], 977 PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': only Basic auth to proxy is supported with HTTP 1.0'); 978 } 979 $proxyCredentials = 'Proxy-Authorization: Basic ' . base64_encode($opts['proxy_user'] . ':' . 980 $opts['proxy_pass']) . "\r\n"; 981 } 982 } else { 983 $connectServer = $server; 984 $connectPort = $port; 985 $transport = ($method === 'https') ? 'tls' : 'tcp'; 986 $uri = $path; 987 } 988 989 // Cookie generation, as per RFC6265 990 // NB: the following code does not honour 'expires', 'path' and 'domain' cookie attributes set to client obj by the user... 991 $cookieHeader = ''; 992 if (count($opts['cookies'])) { 993 $version = ''; 994 foreach ($opts['cookies'] as $name => $cookie) { 995 /// @todo should we sanitize the cookie value on behalf of the user? See setCookie comments 996 $cookieHeader .= ' ' . $name . '=' . $cookie['value'] . ";"; 997 } 998 $cookieHeader = 'Cookie:' . $version . substr($cookieHeader, 0, -1) . "\r\n"; 999 } 1000 1001 // omit port if default 1002 if (($port == 80 && in_array($method, array('http', 'http10'))) || ($port == 443 && $method == 'https')) { 1003 $port = ''; 1004 } else { 1005 $port = ':' . $port; 1006 } 1007 1008 $op = 'POST ' . $uri . " HTTP/1.0\r\n" . 1009 'User-Agent: ' . $opts['user_agent'] . "\r\n" . 1010 'Host: ' . $server . $port . "\r\n" . 1011 $credentials . 1012 $proxyCredentials . 1013 $acceptedEncoding . 1014 $encodingHdr . 1015 'Accept-Charset: ' . implode(',', $opts['accepted_charset_encodings']) . "\r\n" . 1016 $cookieHeader . 1017 'Content-Type: ' . $req->getContentType() . "\r\nContent-Length: " . 1018 strlen($payload) . "\r\n\r\n" . 1019 $payload; 1020 1021 if ($opts['debug'] > 1) { 1022 $this->getLogger()->debug("---SENDING---\n$op\n---END---"); 1023 } 1024 1025 $contextOptions = array(); 1026 if ($method == 'https') { 1027 if ($opts['cert'] != '') { 1028 $contextOptions['ssl']['local_cert'] = $opts['cert']; 1029 if ($opts['certpass'] != '') { 1030 $contextOptions['ssl']['passphrase'] = $opts['certpass']; 1031 } 1032 } 1033 if ($opts['cacert'] != '') { 1034 $contextOptions['ssl']['cafile'] = $opts['cacert']; 1035 } 1036 if ($opts['cacertdir'] != '') { 1037 $contextOptions['ssl']['capath'] = $opts['cacertdir']; 1038 } 1039 if ($opts['key'] != '') { 1040 $contextOptions['ssl']['local_pk'] = $opts['key']; 1041 } 1042 $contextOptions['ssl']['verify_peer'] = $opts['verifypeer']; 1043 $contextOptions['ssl']['verify_peer_name'] = $opts['verifypeer']; 1044 1045 if ($opts['sslversion'] != 0) { 1046 /// @see https://www.php.net/manual/en/function.curl-setopt.php, https://www.php.net/manual/en/migration56.openssl.php 1047 switch($opts['sslversion']) { 1048 /// @todo what does this map to? 1.0-1.3? 1049 //case 1: // TLSv1 1050 // break; 1051 case 2: // SSLv2 1052 $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_SSLv2_CLIENT; 1053 break; 1054 case 3: // SSLv3 1055 $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_SSLv3_CLIENT; 1056 break; 1057 case 4: // TLSv1.0 1058 $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT; 1059 break; 1060 case 5: // TLSv1.1 1061 $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; 1062 break; 1063 case 6: // TLSv1.2 1064 $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 1065 break; 1066 case 7: // TLSv1.3 1067 if (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT')) { 1068 $contextOptions['ssl']['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT; 1069 } else { 1070 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], 1071 PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': TLS-1.3 only is supported with PHP 7.4 or later'); 1072 } 1073 break; 1074 default: 1075 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['unsupported_option'], 1076 PhpXmlRpc::$xmlrpcerr['unsupported_option'] . ': Unsupported required TLS version'); 1077 } 1078 } 1079 } 1080 1081 foreach ($opts['extracurlopts'] as $proto => $protoOpts) { 1082 foreach ($protoOpts as $key => $val) { 1083 $contextOptions[$proto][$key] = $val; 1084 } 1085 } 1086 1087 $context = stream_context_create($contextOptions); 1088 1089 if ($opts['timeout'] <= 0) { 1090 $connectTimeout = ini_get('default_socket_timeout'); 1091 } else { 1092 $connectTimeout = $opts['timeout']; 1093 } 1094 1095 $this->errno = 0; 1096 $this->errstr = ''; 1097 1098 $fp = @stream_socket_client("$transport://$connectServer:$connectPort", $this->errno, $this->errstr, $connectTimeout, 1099 STREAM_CLIENT_CONNECT, $context); 1100 if ($fp) { 1101 if ($opts['timeout'] > 0) { 1102 stream_set_timeout($fp, $opts['timeout'], 0); 1103 } 1104 } else { 1105 if ($this->errstr == '') { 1106 $err = error_get_last(); 1107 $this->errstr = $err['message']; 1108 } 1109 1110 $this->errstr = 'Connect error: ' . $this->errstr; 1111 $r = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr . ' (' . $this->errno . ')'); 1112 1113 return $r; 1114 } 1115 1116 if (!fputs($fp, $op, strlen($op))) { 1117 fclose($fp); 1118 $this->errstr = 'Write error'; 1119 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['http_error'], $this->errstr); 1120 } 1121 1122 // Close socket before parsing. 1123 // It should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects) 1124 $ipd = ''; 1125 do { 1126 // shall we check for $data === FALSE? 1127 // as per the manual, it signals an error 1128 $ipd .= fread($fp, 32768); 1129 } while (!feof($fp)); 1130 fclose($fp); 1131 1132 return $req->parseResponse($ipd, false, $opts['return_type']); 1133 } 1134 1135 /** 1136 * Contributed by Justin Miller 1137 * Requires curl to be built into PHP 1138 * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers! 1139 * 1140 * @param Request $req 1141 * @param string $method 1142 * @param string $server 1143 * @param int $port 1144 * @param string $path 1145 * @param array $opts the keys/values match self::getOptions 1146 * @return Response 1147 * 1148 * @todo the $path arg atm is ignored. What to do if it is != $this->path? 1149 */ 1150 protected function sendViaCURL($req, $method, $server, $port, $path, $opts) 1151 { 1152 if (!function_exists('curl_init')) { 1153 $this->errstr = 'CURL unavailable on this install'; 1154 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_curl'], PhpXmlRpc::$xmlrpcstr['no_curl']); 1155 } 1156 if ($method == 'https' || $method == 'h2') { 1157 // q: what about installs where we get back a string, but curl is linked to other ssl libs than openssl? 1158 if (($info = curl_version()) && 1159 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))) 1160 ) { 1161 $this->errstr = 'SSL unavailable on this install'; 1162 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_ssl'], PhpXmlRpc::$xmlrpcstr['no_ssl']); 1163 } 1164 } 1165 if (($method == 'h2' && !defined('CURL_HTTP_VERSION_2_0')) || 1166 ($method == 'h2c' && !defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'))) { 1167 $this->errstr = 'HTTP/2 unavailable on this install'; 1168 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['no_http2'], PhpXmlRpc::$xmlrpcstr['no_http2']); 1169 } 1170 1171 // BC - we go through prepareCurlHandle in case some subclass reimplemented it 1172 $curl = $this->prepareCurlHandle($req, $server, $port, $opts['timeout'], $opts['username'], $opts['password'], 1173 $opts['authtype'], $opts['cert'], $opts['certpass'], $opts['cacert'], $opts['cacertdir'], $opts['proxy'], 1174 $opts['proxyport'], $opts['proxy_user'], $opts['proxy_pass'], $opts['proxy_authtype'], $method, 1175 $opts['keepalive'], $opts['key'], $opts['keypass'], $opts['sslversion']); 1176 1177 if (!$curl) { 1178 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] . 1179 ': error during curl initialization. Check php error log for details'); 1180 } 1181 1182 $result = curl_exec($curl); 1183 1184 if ($opts['debug'] > 1) { 1185 $message = "---CURL INFO---\n"; 1186 foreach (curl_getinfo($curl) as $name => $val) { 1187 if (is_array($val)) { 1188 $val = implode("\n", $val); 1189 } 1190 $message .= $name . ': ' . $val . "\n"; 1191 } 1192 $message .= '---END---'; 1193 $this->getLogger()->debug($message); 1194 } 1195 1196 if (!$result) { 1197 /// @todo we should use a better check here - what if we get back '' or '0'? 1198 1199 $this->errstr = 'no response'; 1200 $resp = new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['curl_fail'], PhpXmlRpc::$xmlrpcstr['curl_fail'] . 1201 ': ' . curl_error($curl)); 1202 curl_close($curl); 1203 if ($opts['keepalive']) { 1204 $this->xmlrpc_curl_handle = null; 1205 } 1206 } else { 1207 if (!$opts['keepalive']) { 1208 curl_close($curl); 1209 } 1210 $resp = $req->parseResponse($result, true, $opts['return_type']); 1211 if ($opts['keepalive']) { 1212 /// @todo if we got back a 302 or 308, we should not reuse the curl handle for later calls 1213 if ($resp->faultCode() == PhpXmlRpc::$xmlrpcerr['http_error']) { 1214 curl_close($curl); 1215 $this->xmlrpc_curl_handle = null; 1216 } 1217 } 1218 } 1219 1220 return $resp; 1221 } 1222 1223 /** 1224 * @param Request $req 1225 * @param string $method 1226 * @param string $server 1227 * @param int $port 1228 * @param string $path 1229 * @param array $opts the keys/values match self::getOptions 1230 * @return \CurlHandle|resource|false 1231 * 1232 * @todo allow this method to either throw or return a Response, so that we can pass back to caller more info on errors 1233 */ 1234 protected function createCURLHandle($req, $method, $server, $port, $path, $opts) 1235 { 1236 if ($port == 0) { 1237 if (in_array($method, array('http', 'http10', 'http11', 'h2c'))) { 1238 $port = 80; 1239 } else { 1240 $port = 443; 1241 } 1242 } 1243 1244 // Only create the payload if it was not created previously 1245 $payload = $req->getPayload(); 1246 if (empty($payload)) { 1247 $payload = $req->serialize($opts['request_charset_encoding']); 1248 } 1249 1250 // Deflate request body and set appropriate request headers 1251 $encodingHdr = ''; 1252 if (($opts['request_compression'] == 'gzip' || $opts['request_compression'] == 'deflate')) { 1253 if ($opts['request_compression'] == 'gzip' && function_exists('gzencode')) { 1254 $a = @gzencode($payload); 1255 if ($a) { 1256 $payload = $a; 1257 $encodingHdr = 'Content-Encoding: gzip'; 1258 } else { 1259 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzencode failure in compressing request'); 1260 } 1261 } else if (function_exists('gzcompress')) { 1262 $a = @gzcompress($payload); 1263 if ($a) { 1264 $payload = $a; 1265 $encodingHdr = 'Content-Encoding: deflate'; 1266 } else { 1267 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': gzcompress failure in compressing request'); 1268 } 1269 } else { 1270 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported by this PHP install'); 1271 } 1272 } else { 1273 if ($opts['request_compression'] != '') { 1274 $this->getLogger()->warning('XML-RPC: ' . __METHOD__ . ': desired request compression method is unsupported'); 1275 } 1276 } 1277 1278 if (!$opts['keepalive'] || !$this->xmlrpc_curl_handle) { 1279 if ($method == 'http11' || $method == 'http10' || $method == 'h2c') { 1280 $protocol = 'http'; 1281 } else { 1282 if ($method == 'h2') { 1283 $protocol = 'https'; 1284 } else { 1285 // http, https 1286 $protocol = $method; 1287 if (strpos($protocol, ':') !== false) { 1288 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ": warning - attempted hacking attempt?. The curl protocol requested for the call is: '$protocol'"); 1289 return false; 1290 } 1291 } 1292 } 1293 $curl = curl_init($protocol . '://' . $server . ':' . $port . $path); 1294 if (!$curl) { 1295 return false; 1296 } 1297 if ($opts['keepalive']) { 1298 $this->xmlrpc_curl_handle = $curl; 1299 } 1300 } else { 1301 $curl = $this->xmlrpc_curl_handle; 1302 } 1303 1304 // results into variable 1305 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 1306 1307 if ($opts['debug'] > 1) { 1308 curl_setopt($curl, CURLOPT_VERBOSE, true); 1309 /// @todo redirect curlopt_stderr to some stream which can be piped to the logger 1310 } 1311 curl_setopt($curl, CURLOPT_USERAGENT, $opts['user_agent']); 1312 // required for XMLRPC: post the data 1313 curl_setopt($curl, CURLOPT_POST, 1); 1314 // the data 1315 curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); 1316 1317 // return the header too 1318 curl_setopt($curl, CURLOPT_HEADER, 1); 1319 1320 // NB: if we set an empty string, CURL will add http header indicating 1321 // ALL methods it is supporting. This is possibly a better option than letting the user tell what curl can / cannot do... 1322 if (is_array($opts['accepted_compression']) && count($opts['accepted_compression'])) { 1323 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $opts['accepted_compression'])); 1324 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 1325 if (count($opts['accepted_compression']) == 1) { 1326 curl_setopt($curl, CURLOPT_ENCODING, $opts['accepted_compression'][0]); 1327 } else { 1328 curl_setopt($curl, CURLOPT_ENCODING, ''); 1329 } 1330 } 1331 // extra headers 1332 $headers = array('Content-Type: ' . $req->getContentType(), 'Accept-Charset: ' . implode(',', $opts['accepted_charset_encodings'])); 1333 // if no keepalive is wanted, let the server know it in advance 1334 if (!$opts['keepalive']) { 1335 $headers[] = 'Connection: close'; 1336 } 1337 // request compression header 1338 if ($encodingHdr) { 1339 $headers[] = $encodingHdr; 1340 } 1341 1342 // Fix the HTTP/1.1 417 Expectation Failed Bug (curl by default adds a 'Expect: 100-continue' header when POST 1343 // size exceeds 1025 bytes, apparently) 1344 $headers[] = 'Expect:'; 1345 1346 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 1347 // timeout is borked 1348 if ($opts['timeout']) { 1349 curl_setopt($curl, CURLOPT_TIMEOUT, $opts['timeout'] == 1 ? 1 : $opts['timeout'] - 1); 1350 } 1351 1352 switch ($method) { 1353 case 'http10': 1354 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 1355 break; 1356 case 'http11': 1357 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 1358 break; 1359 case 'h2c': 1360 if (defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE')) { 1361 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); 1362 } else { 1363 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. HTTP2 is not supported by the current PHP/curl install'); 1364 curl_close($curl); 1365 return false; 1366 } 1367 break; 1368 case 'h2': 1369 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 1370 break; 1371 } 1372 1373 if ($opts['username'] && $opts['password']) { 1374 curl_setopt($curl, CURLOPT_USERPWD, $opts['username'] . ':' . $opts['password']); 1375 if (defined('CURLOPT_HTTPAUTH')) { 1376 curl_setopt($curl, CURLOPT_HTTPAUTH, $opts['authtype']); 1377 } elseif ($opts['authtype'] != 1) { 1378 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth is supported by the current PHP/curl install'); 1379 curl_close($curl); 1380 return false; 1381 } 1382 } 1383 1384 // note: h2c is http2 without the https. No need to have it in this IF 1385 if ($method == 'https' || $method == 'h2') { 1386 // set cert file 1387 if ($opts['cert']) { 1388 curl_setopt($curl, CURLOPT_SSLCERT, $opts['cert']); 1389 } 1390 // set cert password 1391 if ($opts['certpass']) { 1392 curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $opts['certpass']); 1393 } 1394 // whether to verify remote host's cert 1395 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $opts['verifypeer']); 1396 // set ca certificates file/dir 1397 if ($opts['cacert']) { 1398 curl_setopt($curl, CURLOPT_CAINFO, $opts['cacert']); 1399 } 1400 if ($opts['cacertdir']) { 1401 curl_setopt($curl, CURLOPT_CAPATH, $opts['cacertdir']); 1402 } 1403 // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 1404 if ($opts['key']) { 1405 curl_setopt($curl, CURLOPT_SSLKEY, $opts['key']); 1406 } 1407 // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 1408 if ($opts['keypass']) { 1409 curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $opts['keypass']); 1410 } 1411 // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that 1412 // it matches the hostname used 1413 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $opts['verifyhost']); 1414 // allow usage of different SSL versions 1415 curl_setopt($curl, CURLOPT_SSLVERSION, $opts['sslversion']); 1416 } 1417 1418 // proxy info 1419 if ($opts['proxy']) { 1420 if ($opts['proxyport'] == 0) { 1421 $opts['proxyport'] = 8080; // NB: even for HTTPS, local connection is on port 8080 1422 } 1423 curl_setopt($curl, CURLOPT_PROXY, $opts['proxy'] . ':' . $opts['proxyport']); 1424 if ($opts['proxy_user']) { 1425 curl_setopt($curl, CURLOPT_PROXYUSERPWD, $opts['proxy_user'] . ':' . $opts['proxy_pass']); 1426 if (defined('CURLOPT_PROXYAUTH')) { 1427 curl_setopt($curl, CURLOPT_PROXYAUTH, $opts['proxy_authtype']); 1428 } elseif ($opts['proxy_authtype'] != 1) { 1429 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': warning. Only Basic auth to proxy is supported by the current PHP/curl install'); 1430 curl_close($curl); 1431 return false; 1432 } 1433 } 1434 } 1435 1436 // NB: should we build cookie http headers by hand rather than let CURL do it? 1437 // NB: the following code does not honour 'expires', 'path' and 'domain' cookie attributes set to client obj by the user... 1438 if (count($opts['cookies'])) { 1439 $cookieHeader = ''; 1440 foreach ($opts['cookies'] as $name => $cookie) { 1441 $cookieHeader .= $name . '=' . $cookie['value'] . '; '; 1442 } 1443 curl_setopt($curl, CURLOPT_COOKIE, substr($cookieHeader, 0, -2)); 1444 } 1445 1446 foreach ($opts['extracurlopts'] as $opt => $val) { 1447 curl_setopt($curl, $opt, $val); 1448 } 1449 1450 if ($opts['debug'] > 1) { 1451 $this->getLogger()->debug("---SENDING---\n$payload\n---END---"); 1452 } 1453 1454 return $curl; 1455 } 1456 1457 /** 1458 * Send an array of requests and return an array of responses. 1459 * 1460 * Unless $this->no_multicall has been set to true, it will try first to use one single xml-rpc call to server method 1461 * system.multicall, and revert to sending many successive calls in case of failure. 1462 * This failure is also stored in $this->no_multicall for subsequent calls. 1463 * Unfortunately, there is no server error code universally used to denote the fact that multicall is unsupported, 1464 * so there is no way to reliably distinguish between that and a temporary failure. 1465 * If you are sure that server supports multicall and do not want to fallback to using many single calls, set the 1466 * 2np parameter to FALSE. 1467 * 1468 * NB: trying to shoehorn extra functionality into existing syntax has resulted 1469 * in pretty much convoluted code... 1470 * 1471 * @param Request[] $reqs an array of Request objects 1472 * @param bool $noFallback When true, upon receiving an error during multicall, multiple single calls will not be 1473 * attempted. 1474 * Deprecated alternative, was: int - "connection timeout (in seconds). See the details in the 1475 * docs for the send() method". Please use setOption instead to set a timeout 1476 * @param string $method deprecated. Was: "the http protocol variant to be used. See the details in the docs for the send() method." 1477 * Please use the constructor to set an http protocol variant. 1478 * @param boolean $fallback deprecated. Was: "w"hen true, upon receiving an error during multicall, multiple single 1479 * calls will be attempted" 1480 * @return Response[] 1481 */ 1482 public function multicall($reqs, $timeout = 0, $method = '', $fallback = true) 1483 { 1484 // BC 1485 if (is_bool($timeout) && $fallback === true) { 1486 $fallback = !$timeout; 1487 $timeout = 0; 1488 } 1489 1490 if ($method == '') { 1491 $method = $this->method; 1492 } 1493 1494 if (!$this->no_multicall) { 1495 $results = $this->_try_multicall($reqs, $timeout, $method); 1496 /// @todo how to handle the case of $this->return_type = xml? 1497 if (is_array($results)) { 1498 // System.multicall succeeded 1499 return $results; 1500 } else { 1501 // either system.multicall is unsupported by server, or the call failed for some other reason. 1502 // Feature creep: is there a way to tell apart unsupported multicall from other faults? 1503 if ($fallback) { 1504 // Don't try it next time... 1505 $this->no_multicall = true; 1506 } else { 1507 $result = $results; 1508 } 1509 } 1510 } else { 1511 // override fallback, in case careless user tries to do two 1512 // opposite things at the same time 1513 $fallback = true; 1514 } 1515 1516 $results = array(); 1517 if ($fallback) { 1518 // system.multicall is (probably) unsupported by server: emulate multicall via multiple requests 1519 /// @todo use curl multi_ functions to make this quicker (see the implementation in the parallel.php demo) 1520 foreach ($reqs as $req) { 1521 $results[] = $this->send($req, $timeout, $method); 1522 } 1523 } else { 1524 // user does NOT want to fallback on many single calls: since we should always return an array of responses, 1525 // we return an array with the same error repeated n times 1526 foreach ($reqs as $req) { 1527 $results[] = $result; 1528 } 1529 } 1530 1531 return $results; 1532 } 1533 1534 /** 1535 * Attempt to boxcar $reqs via system.multicall. 1536 * 1537 * @param Request[] $reqs 1538 * @param int $timeout 1539 * @param string $method 1540 * @return Response[]|Response a single Response when the call returned a fault / does not conform to what we expect 1541 * from a multicall response 1542 */ 1543 private function _try_multicall($reqs, $timeout, $method) 1544 { 1545 // Construct multicall request 1546 $calls = array(); 1547 foreach ($reqs as $req) { 1548 $call['methodName'] = new Value($req->method(), 'string'); 1549 $numParams = $req->getNumParams(); 1550 $params = array(); 1551 for ($i = 0; $i < $numParams; $i++) { 1552 $params[$i] = $req->getParam($i); 1553 } 1554 $call['params'] = new Value($params, 'array'); 1555 $calls[] = new Value($call, 'struct'); 1556 } 1557 $multiCall = new static::$requestClass('system.multicall'); 1558 $multiCall->addParam(new Value($calls, 'array')); 1559 1560 // Attempt RPC call 1561 $result = $this->send($multiCall, $timeout, $method); 1562 1563 if ($result->faultCode() != 0) { 1564 // call to system.multicall failed 1565 return $result; 1566 } 1567 1568 // Unpack responses. 1569 $rets = $result->value(); 1570 $response = array(); 1571 1572 if ($this->return_type == 'xml') { 1573 for ($i = 0; $i < count($reqs); $i++) { 1574 $response[] = new static::$responseClass($rets, 0, '', 'xml', $result->httpResponse()); 1575 } 1576 1577 } elseif ($this->return_type == 'phpvals') { 1578 if (!is_array($rets)) { 1579 // bad return type from system.multicall 1580 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1581 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': not an array', 'phpvals', $result->httpResponse()); 1582 } 1583 $numRets = count($rets); 1584 if ($numRets != count($reqs)) { 1585 // wrong number of return values. 1586 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1587 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'phpvals', 1588 $result->httpResponse()); 1589 } 1590 1591 for ($i = 0; $i < $numRets; $i++) { 1592 $val = $rets[$i]; 1593 if (!is_array($val)) { 1594 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1595 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct", 1596 'phpvals', $result->httpResponse()); 1597 } 1598 switch (count($val)) { 1599 case 1: 1600 if (!isset($val[0])) { 1601 // Bad value 1602 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1603 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has no value", 1604 'phpvals', $result->httpResponse()); 1605 } 1606 // Normal return value 1607 $response[$i] = new static::$responseClass($val[0], 0, '', 'phpvals', $result->httpResponse()); 1608 break; 1609 case 2: 1610 /// @todo remove usage of @: it is apparently quite slow 1611 $code = @$val['faultCode']; 1612 if (!is_int($code)) { 1613 /// @todo should we check that it is != 0? 1614 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1615 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode", 1616 'phpvals', $result->httpResponse()); 1617 } 1618 $str = @$val['faultString']; 1619 if (!is_string($str)) { 1620 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1621 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no FaultString", 1622 'phpvals', $result->httpResponse()); 1623 } 1624 $response[$i] = new static::$responseClass(0, $code, $str, 'phpvals', $result->httpResponse()); 1625 break; 1626 default: 1627 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1628 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items", 1629 'phpvals', $result->httpResponse()); 1630 } 1631 } 1632 1633 } else { 1634 // return type == 'xmlrpcvals' 1635 if ($rets->kindOf() != 'array') { 1636 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1637 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array", 'xmlrpcvals', 1638 $result->httpResponse()); 1639 } 1640 $numRets = $rets->count(); 1641 if ($numRets != count($reqs)) { 1642 // wrong number of return values. 1643 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1644 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ': incorrect number of responses', 'xmlrpcvals', 1645 $result->httpResponse()); 1646 } 1647 1648 foreach ($rets as $i => $val) { 1649 switch ($val->kindOf()) { 1650 case 'array': 1651 if ($val->count() != 1) { 1652 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1653 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items", 1654 'phpvals', $result->httpResponse()); 1655 } 1656 // Normal return value 1657 $response[] = new static::$responseClass($val[0], 0, '', 'xmlrpcvals', $result->httpResponse()); 1658 break; 1659 case 'struct': 1660 if ($val->count() != 2) { 1661 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1662 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has too many items", 1663 'phpvals', $result->httpResponse()); 1664 } 1665 /** @var Value $code */ 1666 $code = $val['faultCode']; 1667 if ($code->kindOf() != 'scalar' || $code->scalarTyp() != 'int') { 1668 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1669 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode", 1670 'xmlrpcvals', $result->httpResponse()); 1671 } 1672 /** @var Value $str */ 1673 $str = $val['faultString']; 1674 if ($str->kindOf() != 'scalar' || $str->scalarTyp() != 'string') { 1675 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1676 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i has invalid or no faultCode", 1677 'xmlrpcvals', $result->httpResponse()); 1678 } 1679 $response[] = new static::$responseClass(0, $code->scalarVal(), $str->scalarVal(), 'xmlrpcvals', $result->httpResponse()); 1680 break; 1681 default: 1682 return new static::$responseClass(0, PhpXmlRpc::$xmlrpcerr['multicall_error'], 1683 PhpXmlRpc::$xmlrpcstr['multicall_error'] . ": response element $i is not an array or struct", 1684 'xmlrpcvals', $result->httpResponse()); 1685 } 1686 } 1687 } 1688 1689 return $response; 1690 } 1691 1692 // *** BC layer *** 1693 1694 /** 1695 * @deprecated 1696 * 1697 * @param Request $req 1698 * @param string $server 1699 * @param int $port 1700 * @param int $timeout 1701 * @param string $username 1702 * @param string $password 1703 * @param int $authType 1704 * @param string $proxyHost 1705 * @param int $proxyPort 1706 * @param string $proxyUsername 1707 * @param string $proxyPassword 1708 * @param int $proxyAuthType 1709 * @param string $method 1710 * @return Response 1711 */ 1712 protected function sendPayloadHTTP10($req, $server, $port, $timeout = 0, $username = '', $password = '', 1713 $authType = 1, $proxyHost = '', $proxyPort = 0, $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, 1714 $method = 'http') 1715 { 1716 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 1717 1718 return $this->sendPayloadSocket($req, $server, $port, $timeout, $username, $password, $authType, null, null, 1719 null, null, $proxyHost, $proxyPort, $proxyUsername, $proxyPassword, $proxyAuthType, $method); 1720 } 1721 1722 /** 1723 * @deprecated 1724 * 1725 * @param Request $req 1726 * @param string $server 1727 * @param int $port 1728 * @param int $timeout 1729 * @param string $username 1730 * @param string $password 1731 * @param int $authType 1732 * @param string $cert 1733 * @param string $certPass 1734 * @param string $caCert 1735 * @param string $caCertDir 1736 * @param string $proxyHost 1737 * @param int $proxyPort 1738 * @param string $proxyUsername 1739 * @param string $proxyPassword 1740 * @param int $proxyAuthType 1741 * @param bool $keepAlive 1742 * @param string $key 1743 * @param string $keyPass 1744 * @param int $sslVersion 1745 * @return Response 1746 */ 1747 protected function sendPayloadHTTPS($req, $server, $port, $timeout = 0, $username = '', $password = '', 1748 $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, 1749 $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $keepAlive = false, $key = '', $keyPass = '', 1750 $sslVersion = 0) 1751 { 1752 $this->logDeprecation('Method ' . __METHOD__ . ' is deprecated'); 1753 1754 return $this->sendPayloadCURL($req, $server, $port, $timeout, $username, 1755 $password, $authType, $cert, $certPass, $caCert, $caCertDir, $proxyHost, $proxyPort, 1756 $proxyUsername, $proxyPassword, $proxyAuthType, 'https', $keepAlive, $key, $keyPass, $sslVersion); 1757 } 1758 1759 /** 1760 * @deprecated 1761 * 1762 * @param Request $req 1763 * @param string $server 1764 * @param int $port 1765 * @param int $timeout 1766 * @param string $username 1767 * @param string $password 1768 * @param int $authType only value supported is 1 1769 * @param string $cert 1770 * @param string $certPass 1771 * @param string $caCert 1772 * @param string $caCertDir 1773 * @param string $proxyHost 1774 * @param int $proxyPort 1775 * @param string $proxyUsername 1776 * @param string $proxyPassword 1777 * @param int $proxyAuthType only value supported is 1 1778 * @param string $method 'http' (synonym for 'http10'), 'http10' or 'https' 1779 * @param string $key 1780 * @param string $keyPass @todo not implemented yet. 1781 * @param int $sslVersion @todo not implemented yet. See http://php.net/manual/en/migration56.openssl.php 1782 * @return Response 1783 */ 1784 protected function sendPayloadSocket($req, $server, $port, $timeout = 0, $username = '', $password = '', 1785 $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, 1786 $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'http', $key = '', $keyPass = '', 1787 $sslVersion = 0) 1788 { 1789 $this->logDeprecationUnlessCalledBy('send'); 1790 1791 return $this->sendViaSocket($req, $method, $server, $port, $this->path, array( 1792 'accepted_charset_encodings' => $this->accepted_charset_encodings, 1793 'accepted_compression' => $this->accepted_compression, 1794 'authtype' => $authType, 1795 'cacert' => $caCert, 1796 'cacertdir' => $caCertDir, 1797 'cert' => $cert, 1798 'certpass' => $certPass, 1799 'cookies' => $this->cookies, 1800 'debug' => $this->debug, 1801 'extracurlopts' => $this->extracurlopts, 1802 'extrasockopts' => $this->extrasockopts, 1803 'keepalive' => $this->keepalive, 1804 'key' => $key, 1805 'keypass' => $keyPass, 1806 'no_multicall' => $this->no_multicall, 1807 'password' => $password, 1808 'proxy' => $proxyHost, 1809 'proxy_authtype' => $proxyAuthType, 1810 'proxy_pass' => $proxyPassword, 1811 'proxyport' => $proxyPort, 1812 'proxy_user' => $proxyUsername, 1813 'request_charset_encoding' => $this->request_charset_encoding, 1814 'request_compression' => $this->request_compression, 1815 'return_type' => $this->return_type, 1816 'sslversion' => $sslVersion, 1817 'timeout' => $timeout, 1818 'username' => $username, 1819 'user_agent' => $this->user_agent, 1820 'use_curl' => $this->use_curl, 1821 'verifyhost' => $this->verifyhost, 1822 'verifypeer' => $this->verifypeer, 1823 )); 1824 } 1825 1826 /** 1827 * @deprecated 1828 * 1829 * @param Request $req 1830 * @param string $server 1831 * @param int $port 1832 * @param int $timeout 1833 * @param string $username 1834 * @param string $password 1835 * @param int $authType 1836 * @param string $cert 1837 * @param string $certPass 1838 * @param string $caCert 1839 * @param string $caCertDir 1840 * @param string $proxyHost 1841 * @param int $proxyPort 1842 * @param string $proxyUsername 1843 * @param string $proxyPassword 1844 * @param int $proxyAuthType 1845 * @param string $method 'http' (let curl decide), 'http10', 'http11', 'https', 'h2c' or 'h2' 1846 * @param bool $keepAlive 1847 * @param string $key 1848 * @param string $keyPass 1849 * @param int $sslVersion 1850 * @return Response 1851 */ 1852 protected function sendPayloadCURL($req, $server, $port, $timeout = 0, $username = '', $password = '', 1853 $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, 1854 $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'https', $keepAlive = false, $key = '', 1855 $keyPass = '', $sslVersion = 0) 1856 { 1857 $this->logDeprecationUnlessCalledBy('send'); 1858 1859 return $this->sendViaCURL($req, $method, $server, $port, $this->path, array( 1860 'accepted_charset_encodings' => $this->accepted_charset_encodings, 1861 'accepted_compression' => $this->accepted_compression, 1862 'authtype' => $authType, 1863 'cacert' => $caCert, 1864 'cacertdir' => $caCertDir, 1865 'cert' => $cert, 1866 'certpass' => $certPass, 1867 'cookies' => $this->cookies, 1868 'debug' => $this->debug, 1869 'extracurlopts' => $this->extracurlopts, 1870 'extrasockopts' => $this->extrasockopts, 1871 'keepalive' => $keepAlive, 1872 'key' => $key, 1873 'keypass' => $keyPass, 1874 'no_multicall' => $this->no_multicall, 1875 'password' => $password, 1876 'proxy' => $proxyHost, 1877 'proxy_authtype' => $proxyAuthType, 1878 'proxy_pass' => $proxyPassword, 1879 'proxyport' => $proxyPort, 1880 'proxy_user' => $proxyUsername, 1881 'request_charset_encoding' => $this->request_charset_encoding, 1882 'request_compression' => $this->request_compression, 1883 'return_type' => $this->return_type, 1884 'sslversion' => $sslVersion, 1885 'timeout' => $timeout, 1886 'username' => $username, 1887 'user_agent' => $this->user_agent, 1888 'use_curl' => $this->use_curl, 1889 'verifyhost' => $this->verifyhost, 1890 'verifypeer' => $this->verifypeer, 1891 )); 1892 } 1893 1894 /** 1895 * @deprecated 1896 * 1897 * @param $req 1898 * @param $server 1899 * @param $port 1900 * @param $timeout 1901 * @param $username 1902 * @param $password 1903 * @param $authType 1904 * @param $cert 1905 * @param $certPass 1906 * @param $caCert 1907 * @param $caCertDir 1908 * @param $proxyHost 1909 * @param $proxyPort 1910 * @param $proxyUsername 1911 * @param $proxyPassword 1912 * @param $proxyAuthType 1913 * @param $method 1914 * @param $keepAlive 1915 * @param $key 1916 * @param $keyPass 1917 * @param $sslVersion 1918 * @return false|\CurlHandle|resource 1919 */ 1920 protected function prepareCurlHandle($req, $server, $port, $timeout = 0, $username = '', $password = '', 1921 $authType = 1, $cert = '', $certPass = '', $caCert = '', $caCertDir = '', $proxyHost = '', $proxyPort = 0, 1922 $proxyUsername = '', $proxyPassword = '', $proxyAuthType = 1, $method = 'https', $keepAlive = false, $key = '', 1923 $keyPass = '', $sslVersion = 0) 1924 { 1925 $this->logDeprecationUnlessCalledBy('sendViaCURL'); 1926 1927 return $this->createCURLHandle($req, $method, $server, $port, $this->path, array( 1928 'accepted_charset_encodings' => $this->accepted_charset_encodings, 1929 'accepted_compression' => $this->accepted_compression, 1930 'authtype' => $authType, 1931 'cacert' => $caCert, 1932 'cacertdir' => $caCertDir, 1933 'cert' => $cert, 1934 'certpass' => $certPass, 1935 'cookies' => $this->cookies, 1936 'debug' => $this->debug, 1937 'extracurlopts' => $this->extracurlopts, 1938 'keepalive' => $keepAlive, 1939 'key' => $key, 1940 'keypass' => $keyPass, 1941 'no_multicall' => $this->no_multicall, 1942 'password' => $password, 1943 'proxy' => $proxyHost, 1944 'proxy_authtype' => $proxyAuthType, 1945 'proxy_pass' => $proxyPassword, 1946 'proxyport' => $proxyPort, 1947 'proxy_user' => $proxyUsername, 1948 'request_charset_encoding' => $this->request_charset_encoding, 1949 'request_compression' => $this->request_compression, 1950 'return_type' => $this->return_type, 1951 'sslversion' => $sslVersion, 1952 'timeout' => $timeout, 1953 'username' => $username, 1954 'user_agent' => $this->user_agent, 1955 'use_curl' => $this->use_curl, 1956 'verifyhost' => $this->verifyhost, 1957 'verifypeer' => $this->verifypeer, 1958 )); 1959 } 1960 1961 // we have to make this return by ref in order to allow calls such as `$resp->_cookies['name'] = ['value' => 'something'];` 1962 public function &__get($name) 1963 { 1964 if (in_array($name, static::$options)) { 1965 $this->logDeprecation('Getting property Client::' . $name . ' is deprecated'); 1966 return $this->$name; 1967 } 1968 1969 switch ($name) { 1970 case 'errno': 1971 case 'errstr': 1972 case 'method': 1973 case 'server': 1974 case 'port': 1975 case 'path': 1976 $this->logDeprecation('Getting property Client::' . $name . ' is deprecated'); 1977 return $this->$name; 1978 default: 1979 /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout... 1980 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); 1981 trigger_error('Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING); 1982 $result = null; 1983 return $result; 1984 } 1985 } 1986 1987 public function __set($name, $value) 1988 { 1989 if (in_array($name, static::$options)) { 1990 $this->logDeprecation('Setting property Client::' . $name . ' is deprecated'); 1991 $this->$name = $value; 1992 return; 1993 } 1994 1995 switch ($name) { 1996 case 'errno': 1997 case 'errstr': 1998 case 'method': 1999 case 'server': 2000 case 'port': 2001 case 'path': 2002 $this->logDeprecation('Setting property Client::' . $name . ' is deprecated'); 2003 $this->$name = $value; 2004 return; 2005 default: 2006 /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout... 2007 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); 2008 trigger_error('Undefined property via __set(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING); 2009 } 2010 } 2011 2012 public function __isset($name) 2013 { 2014 if (in_array($name, static::$options)) { 2015 $this->logDeprecation('Checking property Client::' . $name . ' is deprecated'); 2016 return isset($this->$name); 2017 } 2018 2019 switch ($name) { 2020 case 'errno': 2021 case 'errstr': 2022 case 'method': 2023 case 'server': 2024 case 'port': 2025 case 'path': 2026 $this->logDeprecation('Checking property Client::' . $name . ' is deprecated'); 2027 return isset($this->$name); 2028 default: 2029 return false; 2030 } 2031 } 2032 2033 public function __unset($name) 2034 { 2035 if (in_array($name, static::$options)) { 2036 $this->logDeprecation('Unsetting property Client::' . $name . ' is deprecated'); 2037 unset($this->$name); 2038 return; 2039 } 2040 2041 switch ($name) { 2042 case 'errno': 2043 case 'errstr': 2044 case 'method': 2045 case 'server': 2046 case 'port': 2047 case 'path': 2048 $this->logDeprecation('Unsetting property Client::' . $name . ' is deprecated'); 2049 unset($this->$name); 2050 return; 2051 default: 2052 /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout... 2053 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); 2054 trigger_error('Undefined property via __unset(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING); 2055 } 2056 } 2057 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body