External Libraries: Update Requests library to version 2.0.0.
This is a major release and contains breaking changes. Most important changes to be aware of for this release: * All code is now namespaced. Though there is a full backward compatibility layer available and the old class names are still supported, using them will generate a deprecation notice (which can be silenced by plugins if they'd need to support multiple WP versions). See the [https://requests.ryanmccue.info/docs/upgrading.html upgrade guide] for more details. * A lot of classes have been marked `final`. This should generally not affect userland code as care has been taken to not apply the `final` keyword to classes which are known to be extended in userland code. * Extensive input validation has been added to Requests. When Requests is used as documented though, this will be unnoticable. * A new `WpOrg\Requests\Requests::has_capabilities()` method has been introduced which can be used to address #37708. * A new `WpOrg\Requests\Response::decode_body()` method has been introduced which may be usable to simplify some of the WP native wrapper code. * Remaining PHP 8.0 compatibility fixed (support for named parameters). * PHP 8.1 compatibility. Release notes: https://github.com/WordPress/Requests/releases/tag/v2.0.0 For a full list of changes in this update, see the Requests GitHub: https://github.com/WordPress/Requests/compare/v1.8.1...v2.0.0 This commit also resolves 2 blocking issues which previously caused the revert of [52244]: * New Requests files are loaded into `wp-includes/Requests/src/`, matching the location of the library. In doing so, filesystems that are case-insensitive are not impacted (see #54582). * Preload: During a Core update, the old Requests files are preloaded into memory before the update deletes the files. Preloading avoids fatal errors noted in #54562. Follow-up to [50842], [51078], [52244], [52315], [52327], [52328]. Props jrf, schlessera, datagutten, wojsmol, dustinrue, soulseekah, szepeviktor. costdev, sergeybiryukov, peterwilsoncc, ironprogrammer, antonvlasenko, hellofromTonya, swissspidy, dd32, azaozz, TobiasBg, audrasjb. Fixes #54504. See #54582, #54562. Built from https://develop.svn.wordpress.org/trunk@54997 git-svn-id: http://core.svn.wordpress.org/trunk@54530 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
1587b0d725
commit
645e753a51
|
@ -865,6 +865,96 @@ $_old_files = array(
|
||||||
'wp-includes/blocks/comments-query-loop',
|
'wp-includes/blocks/comments-query-loop',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores Requests files to be preloaded and deleted.
|
||||||
|
*
|
||||||
|
* For classes/interfaces, use the class/interface name
|
||||||
|
* as the array key.
|
||||||
|
*
|
||||||
|
* All other files/directories should not have a key.
|
||||||
|
*
|
||||||
|
* @since 6.2.0
|
||||||
|
*
|
||||||
|
* @global array $_old_requests_files
|
||||||
|
* @var array
|
||||||
|
* @name $_old_requests_files
|
||||||
|
*/
|
||||||
|
global $_old_requests_files;
|
||||||
|
|
||||||
|
$_old_requests_files = array(
|
||||||
|
// Interfaces.
|
||||||
|
'Requests_Auth' => 'wp-includes/Requests/Auth.php',
|
||||||
|
'Requests_Hooker' => 'wp-includes/Requests/Hooker.php',
|
||||||
|
'Requests_Proxy' => 'wp-includes/Requests/Proxy.php',
|
||||||
|
'Requests_Transport' => 'wp-includes/Requests/Transport.php',
|
||||||
|
|
||||||
|
// Classes.
|
||||||
|
'Requests_Auth_Basic' => 'wp-includes/Requests/Auth/Basic.php',
|
||||||
|
'Requests_Cookie_Jar' => 'wp-includes/Requests/Cookie/Jar.php',
|
||||||
|
'Requests_Exception_HTTP' => 'wp-includes/Requests/Exception/HTTP.php',
|
||||||
|
'Requests_Exception_Transport' => 'wp-includes/Requests/Exception/Transport.php',
|
||||||
|
'Requests_Exception_HTTP_304' => 'wp-includes/Requests/Exception/HTTP/304.php',
|
||||||
|
'Requests_Exception_HTTP_305' => 'wp-includes/Requests/Exception/HTTP/305.php',
|
||||||
|
'Requests_Exception_HTTP_306' => 'wp-includes/Requests/Exception/HTTP/306.php',
|
||||||
|
'Requests_Exception_HTTP_400' => 'wp-includes/Requests/Exception/HTTP/400.php',
|
||||||
|
'Requests_Exception_HTTP_401' => 'wp-includes/Requests/Exception/HTTP/401.php',
|
||||||
|
'Requests_Exception_HTTP_402' => 'wp-includes/Requests/Exception/HTTP/402.php',
|
||||||
|
'Requests_Exception_HTTP_403' => 'wp-includes/Requests/Exception/HTTP/403.php',
|
||||||
|
'Requests_Exception_HTTP_404' => 'wp-includes/Requests/Exception/HTTP/404.php',
|
||||||
|
'Requests_Exception_HTTP_405' => 'wp-includes/Requests/Exception/HTTP/405.php',
|
||||||
|
'Requests_Exception_HTTP_406' => 'wp-includes/Requests/Exception/HTTP/406.php',
|
||||||
|
'Requests_Exception_HTTP_407' => 'wp-includes/Requests/Exception/HTTP/407.php',
|
||||||
|
'Requests_Exception_HTTP_408' => 'wp-includes/Requests/Exception/HTTP/408.php',
|
||||||
|
'Requests_Exception_HTTP_409' => 'wp-includes/Requests/Exception/HTTP/409.php',
|
||||||
|
'Requests_Exception_HTTP_410' => 'wp-includes/Requests/Exception/HTTP/410.php',
|
||||||
|
'Requests_Exception_HTTP_411' => 'wp-includes/Requests/Exception/HTTP/411.php',
|
||||||
|
'Requests_Exception_HTTP_412' => 'wp-includes/Requests/Exception/HTTP/412.php',
|
||||||
|
'Requests_Exception_HTTP_413' => 'wp-includes/Requests/Exception/HTTP/413.php',
|
||||||
|
'Requests_Exception_HTTP_414' => 'wp-includes/Requests/Exception/HTTP/414.php',
|
||||||
|
'Requests_Exception_HTTP_415' => 'wp-includes/Requests/Exception/HTTP/415.php',
|
||||||
|
'Requests_Exception_HTTP_416' => 'wp-includes/Requests/Exception/HTTP/416.php',
|
||||||
|
'Requests_Exception_HTTP_417' => 'wp-includes/Requests/Exception/HTTP/417.php',
|
||||||
|
'Requests_Exception_HTTP_418' => 'wp-includes/Requests/Exception/HTTP/418.php',
|
||||||
|
'Requests_Exception_HTTP_428' => 'wp-includes/Requests/Exception/HTTP/428.php',
|
||||||
|
'Requests_Exception_HTTP_429' => 'wp-includes/Requests/Exception/HTTP/429.php',
|
||||||
|
'Requests_Exception_HTTP_431' => 'wp-includes/Requests/Exception/HTTP/431.php',
|
||||||
|
'Requests_Exception_HTTP_500' => 'wp-includes/Requests/Exception/HTTP/500.php',
|
||||||
|
'Requests_Exception_HTTP_501' => 'wp-includes/Requests/Exception/HTTP/501.php',
|
||||||
|
'Requests_Exception_HTTP_502' => 'wp-includes/Requests/Exception/HTTP/502.php',
|
||||||
|
'Requests_Exception_HTTP_503' => 'wp-includes/Requests/Exception/HTTP/503.php',
|
||||||
|
'Requests_Exception_HTTP_504' => 'wp-includes/Requests/Exception/HTTP/504.php',
|
||||||
|
'Requests_Exception_HTTP_505' => 'wp-includes/Requests/Exception/HTTP/505.php',
|
||||||
|
'Requests_Exception_HTTP_511' => 'wp-includes/Requests/Exception/HTTP/511.php',
|
||||||
|
'Requests_Exception_HTTP_Unknown' => 'wp-includes/Requests/Exception/HTTP/Unknown.php',
|
||||||
|
'Requests_Exception_Transport_cURL' => 'wp-includes/Requests/Exception/Transport/cURL.php',
|
||||||
|
'Requests_Proxy_HTTP' => 'wp-includes/Requests/Proxy/HTTP.php',
|
||||||
|
'Requests_Response_Headers' => 'wp-includes/Requests/Response/Headers.php',
|
||||||
|
'Requests_Transport_cURL' => 'wp-includes/Requests/Transport/cURL.php',
|
||||||
|
'Requests_Transport_fsockopen' => 'wp-includes/Requests/Transport/fsockopen.php',
|
||||||
|
'Requests_Utility_CaseInsensitiveDictionary' => 'wp-includes/Requests/Utility/CaseInsensitiveDictionary.php',
|
||||||
|
'Requests_Utility_FilteredIterator' => 'wp-includes/Requests/Utility/FilteredIterator.php',
|
||||||
|
'Requests_Cookie' => 'wp-includes/Requests/Cookie.php',
|
||||||
|
'Requests_Exception' => 'wp-includes/Requests/Exception.php',
|
||||||
|
'Requests_Hooks' => 'wp-includes/Requests/Hooks.php',
|
||||||
|
'Requests_IDNAEncoder' => 'wp-includes/Requests/IDNAEncoder.php',
|
||||||
|
'Requests_IPv6' => 'wp-includes/Requests/IPv6.php',
|
||||||
|
'Requests_IRI' => 'wp-includes/Requests/IRI.php',
|
||||||
|
'Requests_Response' => 'wp-includes/Requests/Response.php',
|
||||||
|
'Requests_SSL' => 'wp-includes/Requests/SSL.php',
|
||||||
|
'Requests_Session' => 'wp-includes/Requests/Session.php',
|
||||||
|
|
||||||
|
// Directories.
|
||||||
|
'wp-includes/Requests/Auth/',
|
||||||
|
'wp-includes/Requests/Cookie/',
|
||||||
|
'wp-includes/Requests/Exception/HTTP/',
|
||||||
|
'wp-includes/Requests/Exception/Transport/',
|
||||||
|
'wp-includes/Requests/Exception/',
|
||||||
|
'wp-includes/Requests/Proxy/',
|
||||||
|
'wp-includes/Requests/Response/',
|
||||||
|
'wp-includes/Requests/Transport/',
|
||||||
|
'wp-includes/Requests/Utility/',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores new files in wp-content to copy
|
* Stores new files in wp-content to copy
|
||||||
*
|
*
|
||||||
|
@ -949,6 +1039,7 @@ $_new_bundled_files = array(
|
||||||
*
|
*
|
||||||
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
|
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
|
||||||
* @global array $_old_files
|
* @global array $_old_files
|
||||||
|
* @global array $_old_requests_files
|
||||||
* @global array $_new_bundled_files
|
* @global array $_new_bundled_files
|
||||||
* @global wpdb $wpdb WordPress database abstraction object.
|
* @global wpdb $wpdb WordPress database abstraction object.
|
||||||
* @global string $wp_version
|
* @global string $wp_version
|
||||||
|
@ -960,10 +1051,18 @@ $_new_bundled_files = array(
|
||||||
* @return string|WP_Error New WordPress version on success, WP_Error on failure.
|
* @return string|WP_Error New WordPress version on success, WP_Error on failure.
|
||||||
*/
|
*/
|
||||||
function update_core( $from, $to ) {
|
function update_core( $from, $to ) {
|
||||||
global $wp_filesystem, $_old_files, $_new_bundled_files, $wpdb;
|
global $wp_filesystem, $_old_files, $_old_requests_files, $_new_bundled_files, $wpdb;
|
||||||
|
|
||||||
set_time_limit( 300 );
|
set_time_limit( 300 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Merge the old Requests files and directories into the `$_old_files`.
|
||||||
|
* Then preload these Requests files first, before the files are deleted
|
||||||
|
* and replaced to ensure the code is in memory if needed.
|
||||||
|
*/
|
||||||
|
$_old_files = array_merge( $_old_files, array_values( $_old_requests_files ) );
|
||||||
|
_preload_old_requests_classes_and_interfaces( $to );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters feedback messages displayed during the core update process.
|
* Filters feedback messages displayed during the core update process.
|
||||||
*
|
*
|
||||||
|
@ -1478,6 +1577,48 @@ function update_core( $from, $to ) {
|
||||||
return $wp_version;
|
return $wp_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preloads old Requests classes and interfaces.
|
||||||
|
*
|
||||||
|
* This function preloads the old Requests code into memory before the
|
||||||
|
* upgrade process deletes the files. Why? Requests code is loaded into
|
||||||
|
* memory via an autoloader, meaning when a class or interface is needed
|
||||||
|
* If a request is in process, Requests could attempt to access code. If
|
||||||
|
* the file is not there, a fatal error could occur. If the file was
|
||||||
|
* replaced, the new code is not compatible with the old, resulting in
|
||||||
|
* a fatal error. Preloading ensures the code is in memory before the
|
||||||
|
* code is updated.
|
||||||
|
*
|
||||||
|
* @since 6.2.0
|
||||||
|
*
|
||||||
|
* @global array $_old_requests_files Requests files to be preloaded.
|
||||||
|
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
|
||||||
|
*
|
||||||
|
* @param string $to Path to old WordPress installation.
|
||||||
|
*/
|
||||||
|
function _preload_old_requests_classes_and_interfaces( $to ) {
|
||||||
|
global $_old_requests_files, $wp_filesystem;
|
||||||
|
|
||||||
|
foreach ( $_old_requests_files as $name => $file ) {
|
||||||
|
// Skip files that aren't interfaces or classes.
|
||||||
|
if ( is_int( $name ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if it's already loaded.
|
||||||
|
if ( class_exists( $name ) || interface_exists( $name ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if the file is missing.
|
||||||
|
if ( ! $wp_filesystem->is_file( $to . $file ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once $to . $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect to the About WordPress page after a successful upgrade.
|
* Redirect to the About WordPress page after a successful upgrade.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Authentication provider interface
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Authentication
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authentication provider interface
|
|
||||||
*
|
|
||||||
* Implement this interface to act as an authentication provider.
|
|
||||||
*
|
|
||||||
* Parameters should be passed via the constructor where possible, as this
|
|
||||||
* makes it much easier for users to use your provider.
|
|
||||||
*
|
|
||||||
* @see Requests_Hooks
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Authentication
|
|
||||||
*/
|
|
||||||
interface Requests_Auth {
|
|
||||||
/**
|
|
||||||
* Register hooks as needed
|
|
||||||
*
|
|
||||||
* This method is called in {@see Requests::request} when the user has set
|
|
||||||
* an instance as the 'auth' option. Use this callback to register all the
|
|
||||||
* hooks you'll need.
|
|
||||||
*
|
|
||||||
* @see Requests_Hooks::register
|
|
||||||
* @param Requests_Hooks $hooks Hook system
|
|
||||||
*/
|
|
||||||
public function register(Requests_Hooks $hooks);
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Basic Authentication provider
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Authentication
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic Authentication provider
|
|
||||||
*
|
|
||||||
* Provides a handler for Basic HTTP authentication via the Authorization
|
|
||||||
* header.
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Authentication
|
|
||||||
*/
|
|
||||||
class Requests_Auth_Basic implements Requests_Auth {
|
|
||||||
/**
|
|
||||||
* Username
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Password
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $pass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
|
|
||||||
* @param array|null $args Array of user and password. Must have exactly two elements
|
|
||||||
*/
|
|
||||||
public function __construct($args = null) {
|
|
||||||
if (is_array($args)) {
|
|
||||||
if (count($args) !== 2) {
|
|
||||||
throw new Requests_Exception('Invalid number of arguments', 'authbasicbadargs');
|
|
||||||
}
|
|
||||||
|
|
||||||
list($this->user, $this->pass) = $args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the necessary callbacks
|
|
||||||
*
|
|
||||||
* @see curl_before_send
|
|
||||||
* @see fsockopen_header
|
|
||||||
* @param Requests_Hooks $hooks Hook system
|
|
||||||
*/
|
|
||||||
public function register(Requests_Hooks $hooks) {
|
|
||||||
$hooks->register('curl.before_send', array($this, 'curl_before_send'));
|
|
||||||
$hooks->register('fsockopen.after_headers', array($this, 'fsockopen_header'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set cURL parameters before the data is sent
|
|
||||||
*
|
|
||||||
* @param resource $handle cURL resource
|
|
||||||
*/
|
|
||||||
public function curl_before_send(&$handle) {
|
|
||||||
curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
||||||
curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add extra headers to the request before sending
|
|
||||||
*
|
|
||||||
* @param string $out HTTP header string
|
|
||||||
*/
|
|
||||||
public function fsockopen_header(&$out) {
|
|
||||||
$out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the authentication string (user:pass)
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getAuthString() {
|
|
||||||
return $this->user . ':' . $this->pass;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Cookie holder object
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Cookies
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cookie holder object
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Cookies
|
|
||||||
*/
|
|
||||||
class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate {
|
|
||||||
/**
|
|
||||||
* Actual item data
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $cookies = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new jar
|
|
||||||
*
|
|
||||||
* @param array $cookies Existing cookie values
|
|
||||||
*/
|
|
||||||
public function __construct($cookies = array()) {
|
|
||||||
$this->cookies = $cookies;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalise cookie data into a Requests_Cookie
|
|
||||||
*
|
|
||||||
* @param string|Requests_Cookie $cookie
|
|
||||||
* @return Requests_Cookie
|
|
||||||
*/
|
|
||||||
public function normalize_cookie($cookie, $key = null) {
|
|
||||||
if ($cookie instanceof Requests_Cookie) {
|
|
||||||
return $cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Requests_Cookie::parse($cookie, $key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalise cookie data into a Requests_Cookie
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @deprecated Use {@see Requests_Cookie_Jar::normalize_cookie}
|
|
||||||
* @return Requests_Cookie
|
|
||||||
*/
|
|
||||||
public function normalizeCookie($cookie, $key = null) {
|
|
||||||
return $this->normalize_cookie($cookie, $key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the given item exists
|
|
||||||
*
|
|
||||||
* @param string $key Item key
|
|
||||||
* @return boolean Does the item exist?
|
|
||||||
*/
|
|
||||||
public function offsetExists($key) {
|
|
||||||
return isset($this->cookies[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the value for the item
|
|
||||||
*
|
|
||||||
* @param string $key Item key
|
|
||||||
* @return string|null Item value (null if offsetExists is false)
|
|
||||||
*/
|
|
||||||
public function offsetGet($key) {
|
|
||||||
if (!isset($this->cookies[$key])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->cookies[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the given item
|
|
||||||
*
|
|
||||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
|
||||||
*
|
|
||||||
* @param string $key Item name
|
|
||||||
* @param string $value Item value
|
|
||||||
*/
|
|
||||||
public function offsetSet($key, $value) {
|
|
||||||
if ($key === null) {
|
|
||||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cookies[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unset the given header
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
*/
|
|
||||||
public function offsetUnset($key) {
|
|
||||||
unset($this->cookies[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator for the data
|
|
||||||
*
|
|
||||||
* @return ArrayIterator
|
|
||||||
*/
|
|
||||||
public function getIterator() {
|
|
||||||
return new ArrayIterator($this->cookies);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the cookie handler with the request's hooking system
|
|
||||||
*
|
|
||||||
* @param Requests_Hooker $hooks Hooking system
|
|
||||||
*/
|
|
||||||
public function register(Requests_Hooker $hooks) {
|
|
||||||
$hooks->register('requests.before_request', array($this, 'before_request'));
|
|
||||||
$hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add Cookie header to a request if we have any
|
|
||||||
*
|
|
||||||
* As per RFC 6265, cookies are separated by '; '
|
|
||||||
*
|
|
||||||
* @param string $url
|
|
||||||
* @param array $headers
|
|
||||||
* @param array $data
|
|
||||||
* @param string $type
|
|
||||||
* @param array $options
|
|
||||||
*/
|
|
||||||
public function before_request($url, &$headers, &$data, &$type, &$options) {
|
|
||||||
if (!$url instanceof Requests_IRI) {
|
|
||||||
$url = new Requests_IRI($url);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($this->cookies)) {
|
|
||||||
$cookies = array();
|
|
||||||
foreach ($this->cookies as $key => $cookie) {
|
|
||||||
$cookie = $this->normalize_cookie($cookie, $key);
|
|
||||||
|
|
||||||
// Skip expired cookies
|
|
||||||
if ($cookie->is_expired()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($cookie->domain_matches($url->host)) {
|
|
||||||
$cookies[] = $cookie->format_for_header();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$headers['Cookie'] = implode('; ', $cookies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse all cookies from a response and attach them to the response
|
|
||||||
*
|
|
||||||
* @var Requests_Response $response
|
|
||||||
*/
|
|
||||||
public function before_redirect_check(Requests_Response $return) {
|
|
||||||
$url = $return->url;
|
|
||||||
if (!$url instanceof Requests_IRI) {
|
|
||||||
$url = new Requests_IRI($url);
|
|
||||||
}
|
|
||||||
|
|
||||||
$cookies = Requests_Cookie::parse_from_headers($return->headers, $url);
|
|
||||||
$this->cookies = array_merge($this->cookies, $cookies);
|
|
||||||
$return->cookies = $this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Exception for 429 Too Many Requests responses
|
|
||||||
*
|
|
||||||
* @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04
|
|
||||||
* @package Requests
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception for 429 Too Many Requests responses
|
|
||||||
*
|
|
||||||
* @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04
|
|
||||||
* @package Requests
|
|
||||||
*/
|
|
||||||
class Requests_Exception_HTTP_429 extends Requests_Exception_HTTP {
|
|
||||||
/**
|
|
||||||
* HTTP status code
|
|
||||||
*
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
protected $code = 429;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reason phrase
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $reason = 'Too Many Requests';
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
class Requests_Exception_Transport extends Requests_Exception {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Handles adding and dispatching events
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles adding and dispatching events
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
|
||||||
class Requests_Hooks implements Requests_Hooker {
|
|
||||||
/**
|
|
||||||
* Registered callbacks for each hook
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $hooks = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
// pass
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a callback for a hook
|
|
||||||
*
|
|
||||||
* @param string $hook Hook name
|
|
||||||
* @param callback $callback Function/method to call on event
|
|
||||||
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
|
||||||
*/
|
|
||||||
public function register($hook, $callback, $priority = 0) {
|
|
||||||
if (!isset($this->hooks[$hook])) {
|
|
||||||
$this->hooks[$hook] = array();
|
|
||||||
}
|
|
||||||
if (!isset($this->hooks[$hook][$priority])) {
|
|
||||||
$this->hooks[$hook][$priority] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->hooks[$hook][$priority][] = $callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatch a message
|
|
||||||
*
|
|
||||||
* @param string $hook Hook name
|
|
||||||
* @param array $parameters Parameters to pass to callbacks
|
|
||||||
* @return boolean Successfulness
|
|
||||||
*/
|
|
||||||
public function dispatch($hook, $parameters = array()) {
|
|
||||||
if (empty($this->hooks[$hook])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->hooks[$hook] as $priority => $hooked) {
|
|
||||||
foreach ($hooked as $callback) {
|
|
||||||
call_user_func_array($callback, $parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Proxy connection interface
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Proxy
|
|
||||||
* @since 1.6
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Proxy connection interface
|
|
||||||
*
|
|
||||||
* Implement this interface to handle proxy settings and authentication
|
|
||||||
*
|
|
||||||
* Parameters should be passed via the constructor where possible, as this
|
|
||||||
* makes it much easier for users to use your provider.
|
|
||||||
*
|
|
||||||
* @see Requests_Hooks
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Proxy
|
|
||||||
* @since 1.6
|
|
||||||
*/
|
|
||||||
interface Requests_Proxy {
|
|
||||||
/**
|
|
||||||
* Register hooks as needed
|
|
||||||
*
|
|
||||||
* This method is called in {@see Requests::request} when the user has set
|
|
||||||
* an instance as the 'auth' option. Use this callback to register all the
|
|
||||||
* hooks you'll need.
|
|
||||||
*
|
|
||||||
* @see Requests_Hooks::register
|
|
||||||
* @param Requests_Hooks $hooks Hook system
|
|
||||||
*/
|
|
||||||
public function register(Requests_Hooks $hooks);
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* HTTP response class
|
|
||||||
*
|
|
||||||
* Contains a response from Requests::request()
|
|
||||||
* @package Requests
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP response class
|
|
||||||
*
|
|
||||||
* Contains a response from Requests::request()
|
|
||||||
* @package Requests
|
|
||||||
*/
|
|
||||||
class Requests_Response {
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
$this->headers = new Requests_Response_Headers();
|
|
||||||
$this->cookies = new Requests_Cookie_Jar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response body
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $body = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raw HTTP data from the transport
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $raw = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Headers, as an associative array
|
|
||||||
*
|
|
||||||
* @var Requests_Response_Headers Array-like object representing headers
|
|
||||||
*/
|
|
||||||
public $headers = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status code, false if non-blocking
|
|
||||||
*
|
|
||||||
* @var integer|boolean
|
|
||||||
*/
|
|
||||||
public $status_code = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Protocol version, false if non-blocking
|
|
||||||
*
|
|
||||||
* @var float|boolean
|
|
||||||
*/
|
|
||||||
public $protocol_version = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the request succeeded or not
|
|
||||||
*
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
public $success = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of redirects the request used
|
|
||||||
*
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
public $redirects = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL requested
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $url = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Previous requests (from redirects)
|
|
||||||
*
|
|
||||||
* @var array Array of Requests_Response objects
|
|
||||||
*/
|
|
||||||
public $history = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cookies from the request
|
|
||||||
*
|
|
||||||
* @var Requests_Cookie_Jar Array-like object representing a cookie jar
|
|
||||||
*/
|
|
||||||
public $cookies = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the response a redirect?
|
|
||||||
*
|
|
||||||
* @return boolean True if redirect (3xx status), false if not.
|
|
||||||
*/
|
|
||||||
public function is_redirect() {
|
|
||||||
$code = $this->status_code;
|
|
||||||
return in_array($code, array(300, 301, 302, 303, 307), true) || $code > 307 && $code < 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throws an exception if the request was not successful
|
|
||||||
*
|
|
||||||
* @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`)
|
|
||||||
* @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404})
|
|
||||||
* @param boolean $allow_redirects Set to false to throw on a 3xx as well
|
|
||||||
*/
|
|
||||||
public function throw_for_status($allow_redirects = true) {
|
|
||||||
if ($this->is_redirect()) {
|
|
||||||
if (!$allow_redirects) {
|
|
||||||
throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif (!$this->success) {
|
|
||||||
$exception = Requests_Exception_HTTP::get_class($this->status_code);
|
|
||||||
throw new $exception(null, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Case-insensitive dictionary, suitable for HTTP headers
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Case-insensitive dictionary, suitable for HTTP headers
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
*/
|
|
||||||
class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictionary {
|
|
||||||
/**
|
|
||||||
* Get the given header
|
|
||||||
*
|
|
||||||
* Unlike {@see self::getValues()}, this returns a string. If there are
|
|
||||||
* multiple values, it concatenates them with a comma as per RFC2616.
|
|
||||||
*
|
|
||||||
* Avoid using this where commas may be used unquoted in values, such as
|
|
||||||
* Set-Cookie headers.
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return string|null Header value
|
|
||||||
*/
|
|
||||||
public function offsetGet($key) {
|
|
||||||
$key = strtolower($key);
|
|
||||||
if (!isset($this->data[$key])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->flatten($this->data[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the given item
|
|
||||||
*
|
|
||||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
|
||||||
*
|
|
||||||
* @param string $key Item name
|
|
||||||
* @param string $value Item value
|
|
||||||
*/
|
|
||||||
public function offsetSet($key, $value) {
|
|
||||||
if ($key === null) {
|
|
||||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
if (!isset($this->data[$key])) {
|
|
||||||
$this->data[$key] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->data[$key][] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all values for a given header
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return array|null Header values
|
|
||||||
*/
|
|
||||||
public function getValues($key) {
|
|
||||||
$key = strtolower($key);
|
|
||||||
if (!isset($this->data[$key])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->data[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flattens a value into a string
|
|
||||||
*
|
|
||||||
* Converts an array into a string by imploding values with a comma, as per
|
|
||||||
* RFC2616's rules for folding headers.
|
|
||||||
*
|
|
||||||
* @param string|array $value Value to flatten
|
|
||||||
* @return string Flattened value
|
|
||||||
*/
|
|
||||||
public function flatten($value) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
$value = implode(',', $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator for the data
|
|
||||||
*
|
|
||||||
* Converts the internal
|
|
||||||
* @return ArrayIterator
|
|
||||||
*/
|
|
||||||
public function getIterator() {
|
|
||||||
return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten'));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Base HTTP transport
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Transport
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base HTTP transport
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Transport
|
|
||||||
*/
|
|
||||||
interface Requests_Transport {
|
|
||||||
/**
|
|
||||||
* Perform a request
|
|
||||||
*
|
|
||||||
* @param string $url URL to request
|
|
||||||
* @param array $headers Associative array of request headers
|
|
||||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
|
||||||
* @param array $options Request options, see {@see Requests::response()} for documentation
|
|
||||||
* @return string Raw HTTP result
|
|
||||||
*/
|
|
||||||
public function request($url, $headers = array(), $data = array(), $options = array());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send multiple requests simultaneously
|
|
||||||
*
|
|
||||||
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request}
|
|
||||||
* @param array $options Global options, see {@see Requests::response()} for documentation
|
|
||||||
* @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
|
|
||||||
*/
|
|
||||||
public function request_multiple($requests, $options);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Self-test whether the transport can be used
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function test();
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Case-insensitive dictionary, suitable for HTTP headers
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Case-insensitive dictionary, suitable for HTTP headers
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
|
||||||
class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
|
|
||||||
/**
|
|
||||||
* Actual item data
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $data = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a case insensitive dictionary.
|
|
||||||
*
|
|
||||||
* @param array $data Dictionary/map to convert to case-insensitive
|
|
||||||
*/
|
|
||||||
public function __construct(array $data = array()) {
|
|
||||||
foreach ($data as $key => $value) {
|
|
||||||
$this->offsetSet($key, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the given item exists
|
|
||||||
*
|
|
||||||
* @param string $key Item key
|
|
||||||
* @return boolean Does the item exist?
|
|
||||||
*/
|
|
||||||
public function offsetExists($key) {
|
|
||||||
$key = strtolower($key);
|
|
||||||
return isset($this->data[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the value for the item
|
|
||||||
*
|
|
||||||
* @param string $key Item key
|
|
||||||
* @return string|null Item value (null if offsetExists is false)
|
|
||||||
*/
|
|
||||||
public function offsetGet($key) {
|
|
||||||
$key = strtolower($key);
|
|
||||||
if (!isset($this->data[$key])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->data[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the given item
|
|
||||||
*
|
|
||||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
|
||||||
*
|
|
||||||
* @param string $key Item name
|
|
||||||
* @param string $value Item value
|
|
||||||
*/
|
|
||||||
public function offsetSet($key, $value) {
|
|
||||||
if ($key === null) {
|
|
||||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = strtolower($key);
|
|
||||||
$this->data[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unset the given header
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
*/
|
|
||||||
public function offsetUnset($key) {
|
|
||||||
unset($this->data[strtolower($key)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator for the data
|
|
||||||
*
|
|
||||||
* @return ArrayIterator
|
|
||||||
*/
|
|
||||||
public function getIterator() {
|
|
||||||
return new ArrayIterator($this->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the headers as an array
|
|
||||||
*
|
|
||||||
* @return array Header data
|
|
||||||
*/
|
|
||||||
public function getAll() {
|
|
||||||
return $this->data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Iterator for arrays requiring filtered values
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterator for arrays requiring filtered values
|
|
||||||
*
|
|
||||||
* @package Requests
|
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
|
||||||
class Requests_Utility_FilteredIterator extends ArrayIterator {
|
|
||||||
/**
|
|
||||||
* Callback to run as a filter
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
protected $callback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new iterator
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @param callable $callback Callback to be called on each value
|
|
||||||
*/
|
|
||||||
public function __construct($data, $callback) {
|
|
||||||
parent::__construct($data);
|
|
||||||
|
|
||||||
$this->callback = $callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current item's value after filtering
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function current() {
|
|
||||||
$value = parent::current();
|
|
||||||
|
|
||||||
if (is_callable($this->callback)) {
|
|
||||||
$value = call_user_func($this->callback, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function unserialize($serialized) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*
|
|
||||||
* @phpcs:disable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.MethodDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
|
|
||||||
*/
|
|
||||||
public function __unserialize($serialized) {}
|
|
||||||
|
|
||||||
public function __wakeup() {
|
|
||||||
unset($this->callback);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Authentication provider interface
|
||||||
|
*
|
||||||
|
* @package Requests\Authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Hooks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication provider interface
|
||||||
|
*
|
||||||
|
* Implement this interface to act as an authentication provider.
|
||||||
|
*
|
||||||
|
* Parameters should be passed via the constructor where possible, as this
|
||||||
|
* makes it much easier for users to use your provider.
|
||||||
|
*
|
||||||
|
* @see \WpOrg\Requests\Hooks
|
||||||
|
*
|
||||||
|
* @package Requests\Authentication
|
||||||
|
*/
|
||||||
|
interface Auth {
|
||||||
|
/**
|
||||||
|
* Register hooks as needed
|
||||||
|
*
|
||||||
|
* This method is called in {@see \WpOrg\Requests\Requests::request()} when the user
|
||||||
|
* has set an instance as the 'auth' option. Use this callback to register all the
|
||||||
|
* hooks you'll need.
|
||||||
|
*
|
||||||
|
* @see \WpOrg\Requests\Hooks::register()
|
||||||
|
* @param \WpOrg\Requests\Hooks $hooks Hook system
|
||||||
|
*/
|
||||||
|
public function register(Hooks $hooks);
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Basic Authentication provider
|
||||||
|
*
|
||||||
|
* @package Requests\Authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Auth;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Auth;
|
||||||
|
use WpOrg\Requests\Exception\ArgumentCount;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Hooks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic Authentication provider
|
||||||
|
*
|
||||||
|
* Provides a handler for Basic HTTP authentication via the Authorization
|
||||||
|
* header.
|
||||||
|
*
|
||||||
|
* @package Requests\Authentication
|
||||||
|
*/
|
||||||
|
class Basic implements Auth {
|
||||||
|
/**
|
||||||
|
* Username
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $pass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @since 2.0 Throws an `InvalidArgument` exception.
|
||||||
|
* @since 2.0 Throws an `ArgumentCount` exception instead of the Requests base `Exception.
|
||||||
|
*
|
||||||
|
* @param array|null $args Array of user and password. Must have exactly two elements
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array or null.
|
||||||
|
* @throws \WpOrg\Requests\Exception\ArgumentCount On incorrect number of array elements (`authbasicbadargs`).
|
||||||
|
*/
|
||||||
|
public function __construct($args = null) {
|
||||||
|
if (is_array($args)) {
|
||||||
|
if (count($args) !== 2) {
|
||||||
|
throw ArgumentCount::create('an array with exactly two elements', count($args), 'authbasicbadargs');
|
||||||
|
}
|
||||||
|
|
||||||
|
list($this->user, $this->pass) = $args;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($args !== null) {
|
||||||
|
throw InvalidArgument::create(1, '$args', 'array|null', gettype($args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the necessary callbacks
|
||||||
|
*
|
||||||
|
* @see \WpOrg\Requests\Auth\Basic::curl_before_send()
|
||||||
|
* @see \WpOrg\Requests\Auth\Basic::fsockopen_header()
|
||||||
|
* @param \WpOrg\Requests\Hooks $hooks Hook system
|
||||||
|
*/
|
||||||
|
public function register(Hooks $hooks) {
|
||||||
|
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
|
||||||
|
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cURL parameters before the data is sent
|
||||||
|
*
|
||||||
|
* @param resource|\CurlHandle $handle cURL handle
|
||||||
|
*/
|
||||||
|
public function curl_before_send(&$handle) {
|
||||||
|
curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||||
|
curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add extra headers to the request before sending
|
||||||
|
*
|
||||||
|
* @param string $out HTTP header string
|
||||||
|
*/
|
||||||
|
public function fsockopen_header(&$out) {
|
||||||
|
$out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the authentication string (user:pass)
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAuthString() {
|
||||||
|
return $this->user . ':' . $this->pass;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Autoloader for Requests for PHP.
|
||||||
|
*
|
||||||
|
* Include this file if you'd like to avoid having to create your own autoloader.
|
||||||
|
*
|
||||||
|
* @package Requests
|
||||||
|
* @since 2.0.0
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the autoloader is only declared once.
|
||||||
|
* This safeguard is in place as this is the typical entry point for this library
|
||||||
|
* and this file being required unconditionally could easily cause
|
||||||
|
* fatal "Class already declared" errors.
|
||||||
|
*/
|
||||||
|
if (class_exists('WpOrg\Requests\Autoload') === false) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autoloader for Requests for PHP.
|
||||||
|
*
|
||||||
|
* This autoloader supports the PSR-4 based Requests 2.0.0 classes in a case-sensitive manner
|
||||||
|
* as the most common server OS-es are case-sensitive and the file names are in mixed case.
|
||||||
|
*
|
||||||
|
* For the PSR-0 Requests 1.x BC-layer, requested classes will be treated case-insensitively.
|
||||||
|
*
|
||||||
|
* @package Requests
|
||||||
|
*/
|
||||||
|
final class Autoload {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of the old PSR-0 class names in lowercase as keys with their PSR-4 case-sensitive name as a value.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $deprecated_classes = [
|
||||||
|
// Interfaces.
|
||||||
|
'requests_auth' => '\WpOrg\Requests\Auth',
|
||||||
|
'requests_hooker' => '\WpOrg\Requests\HookManager',
|
||||||
|
'requests_proxy' => '\WpOrg\Requests\Proxy',
|
||||||
|
'requests_transport' => '\WpOrg\Requests\Transport',
|
||||||
|
|
||||||
|
// Classes.
|
||||||
|
'requests_cookie' => '\WpOrg\Requests\Cookie',
|
||||||
|
'requests_exception' => '\WpOrg\Requests\Exception',
|
||||||
|
'requests_hooks' => '\WpOrg\Requests\Hooks',
|
||||||
|
'requests_idnaencoder' => '\WpOrg\Requests\IdnaEncoder',
|
||||||
|
'requests_ipv6' => '\WpOrg\Requests\Ipv6',
|
||||||
|
'requests_iri' => '\WpOrg\Requests\Iri',
|
||||||
|
'requests_response' => '\WpOrg\Requests\Response',
|
||||||
|
'requests_session' => '\WpOrg\Requests\Session',
|
||||||
|
'requests_ssl' => '\WpOrg\Requests\Ssl',
|
||||||
|
'requests_auth_basic' => '\WpOrg\Requests\Auth\Basic',
|
||||||
|
'requests_cookie_jar' => '\WpOrg\Requests\Cookie\Jar',
|
||||||
|
'requests_proxy_http' => '\WpOrg\Requests\Proxy\Http',
|
||||||
|
'requests_response_headers' => '\WpOrg\Requests\Response\Headers',
|
||||||
|
'requests_transport_curl' => '\WpOrg\Requests\Transport\Curl',
|
||||||
|
'requests_transport_fsockopen' => '\WpOrg\Requests\Transport\Fsockopen',
|
||||||
|
'requests_utility_caseinsensitivedictionary' => '\WpOrg\Requests\Utility\CaseInsensitiveDictionary',
|
||||||
|
'requests_utility_filterediterator' => '\WpOrg\Requests\Utility\FilteredIterator',
|
||||||
|
'requests_exception_http' => '\WpOrg\Requests\Exception\Http',
|
||||||
|
'requests_exception_transport' => '\WpOrg\Requests\Exception\Transport',
|
||||||
|
'requests_exception_transport_curl' => '\WpOrg\Requests\Exception\Transport\Curl',
|
||||||
|
'requests_exception_http_304' => '\WpOrg\Requests\Exception\Http\Status304',
|
||||||
|
'requests_exception_http_305' => '\WpOrg\Requests\Exception\Http\Status305',
|
||||||
|
'requests_exception_http_306' => '\WpOrg\Requests\Exception\Http\Status306',
|
||||||
|
'requests_exception_http_400' => '\WpOrg\Requests\Exception\Http\Status400',
|
||||||
|
'requests_exception_http_401' => '\WpOrg\Requests\Exception\Http\Status401',
|
||||||
|
'requests_exception_http_402' => '\WpOrg\Requests\Exception\Http\Status402',
|
||||||
|
'requests_exception_http_403' => '\WpOrg\Requests\Exception\Http\Status403',
|
||||||
|
'requests_exception_http_404' => '\WpOrg\Requests\Exception\Http\Status404',
|
||||||
|
'requests_exception_http_405' => '\WpOrg\Requests\Exception\Http\Status405',
|
||||||
|
'requests_exception_http_406' => '\WpOrg\Requests\Exception\Http\Status406',
|
||||||
|
'requests_exception_http_407' => '\WpOrg\Requests\Exception\Http\Status407',
|
||||||
|
'requests_exception_http_408' => '\WpOrg\Requests\Exception\Http\Status408',
|
||||||
|
'requests_exception_http_409' => '\WpOrg\Requests\Exception\Http\Status409',
|
||||||
|
'requests_exception_http_410' => '\WpOrg\Requests\Exception\Http\Status410',
|
||||||
|
'requests_exception_http_411' => '\WpOrg\Requests\Exception\Http\Status411',
|
||||||
|
'requests_exception_http_412' => '\WpOrg\Requests\Exception\Http\Status412',
|
||||||
|
'requests_exception_http_413' => '\WpOrg\Requests\Exception\Http\Status413',
|
||||||
|
'requests_exception_http_414' => '\WpOrg\Requests\Exception\Http\Status414',
|
||||||
|
'requests_exception_http_415' => '\WpOrg\Requests\Exception\Http\Status415',
|
||||||
|
'requests_exception_http_416' => '\WpOrg\Requests\Exception\Http\Status416',
|
||||||
|
'requests_exception_http_417' => '\WpOrg\Requests\Exception\Http\Status417',
|
||||||
|
'requests_exception_http_418' => '\WpOrg\Requests\Exception\Http\Status418',
|
||||||
|
'requests_exception_http_428' => '\WpOrg\Requests\Exception\Http\Status428',
|
||||||
|
'requests_exception_http_429' => '\WpOrg\Requests\Exception\Http\Status429',
|
||||||
|
'requests_exception_http_431' => '\WpOrg\Requests\Exception\Http\Status431',
|
||||||
|
'requests_exception_http_500' => '\WpOrg\Requests\Exception\Http\Status500',
|
||||||
|
'requests_exception_http_501' => '\WpOrg\Requests\Exception\Http\Status501',
|
||||||
|
'requests_exception_http_502' => '\WpOrg\Requests\Exception\Http\Status502',
|
||||||
|
'requests_exception_http_503' => '\WpOrg\Requests\Exception\Http\Status503',
|
||||||
|
'requests_exception_http_504' => '\WpOrg\Requests\Exception\Http\Status504',
|
||||||
|
'requests_exception_http_505' => '\WpOrg\Requests\Exception\Http\Status505',
|
||||||
|
'requests_exception_http_511' => '\WpOrg\Requests\Exception\Http\Status511',
|
||||||
|
'requests_exception_http_unknown' => '\WpOrg\Requests\Exception\Http\StatusUnknown',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the autoloader.
|
||||||
|
*
|
||||||
|
* Note: the autoloader is *prepended* in the autoload queue.
|
||||||
|
* This is done to ensure that the Requests 2.0 autoloader takes precedence
|
||||||
|
* over a potentially (dependency-registered) Requests 1.x autoloader.
|
||||||
|
*
|
||||||
|
* @internal This method contains a safeguard against the autoloader being
|
||||||
|
* registered multiple times. This safeguard uses a global constant to
|
||||||
|
* (hopefully/in most cases) still function correctly, even if the
|
||||||
|
* class would be renamed.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function register() {
|
||||||
|
if (defined('REQUESTS_AUTOLOAD_REGISTERED') === false) {
|
||||||
|
spl_autoload_register([self::class, 'load'], true);
|
||||||
|
define('REQUESTS_AUTOLOAD_REGISTERED', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autoloader.
|
||||||
|
*
|
||||||
|
* @param string $class_name Name of the class name to load.
|
||||||
|
*
|
||||||
|
* @return bool Whether a class was loaded or not.
|
||||||
|
*/
|
||||||
|
public static function load($class_name) {
|
||||||
|
// Check that the class starts with "Requests" (PSR-0) or "WpOrg\Requests" (PSR-4).
|
||||||
|
$psr_4_prefix_pos = strpos($class_name, 'WpOrg\\Requests\\');
|
||||||
|
|
||||||
|
if (stripos($class_name, 'Requests') !== 0 && $psr_4_prefix_pos !== 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$class_lower = strtolower($class_name);
|
||||||
|
|
||||||
|
if ($class_lower === 'requests') {
|
||||||
|
// Reference to the original PSR-0 Requests class.
|
||||||
|
$file = dirname(__DIR__) . '/library/Requests.php';
|
||||||
|
} elseif ($psr_4_prefix_pos === 0) {
|
||||||
|
// PSR-4 classname.
|
||||||
|
$file = __DIR__ . '/' . strtr(substr($class_name, 15), '\\', '/') . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($file) && file_exists($file)) {
|
||||||
|
include $file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Okay, so the class starts with "Requests", but we couldn't find the file.
|
||||||
|
* If this is one of the deprecated/renamed PSR-0 classes being requested,
|
||||||
|
* let's alias it to the new name and throw a deprecation notice.
|
||||||
|
*/
|
||||||
|
if (isset(self::$deprecated_classes[$class_lower])) {
|
||||||
|
/*
|
||||||
|
* Integrators who cannot yet upgrade to the PSR-4 class names can silence deprecations
|
||||||
|
* by defining a `REQUESTS_SILENCE_PSR0_DEPRECATIONS` constant and setting it to `true`.
|
||||||
|
* The constant needs to be defined before the first deprecated class is requested
|
||||||
|
* via this autoloader.
|
||||||
|
*/
|
||||||
|
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS') || REQUESTS_SILENCE_PSR0_DEPRECATIONS !== true) {
|
||||||
|
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||||
|
trigger_error(
|
||||||
|
'The PSR-0 `Requests_...` class names in the Request library are deprecated.'
|
||||||
|
. ' Switch to the PSR-4 `WpOrg\Requests\...` class names at your earliest convenience.',
|
||||||
|
E_USER_DEPRECATED
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prevent the deprecation notice from being thrown twice.
|
||||||
|
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) {
|
||||||
|
define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an alias and let the autoloader recursively kick in to load the PSR-4 class.
|
||||||
|
return class_alias(self::$deprecated_classes[$class_lower], $class_name, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Capability interface declaring the known capabilities.
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capability interface declaring the known capabilities.
|
||||||
|
*
|
||||||
|
* This is used as the authoritative source for which capabilities can be queried.
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
interface Capability {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for SSL.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const SSL = 'ssl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of all capabilities supported in Requests.
|
||||||
|
*
|
||||||
|
* Note: this does not automatically mean that the capability will be supported for your chosen transport!
|
||||||
|
*
|
||||||
|
* @var array<string>
|
||||||
|
*/
|
||||||
|
const ALL = [
|
||||||
|
self::SSL,
|
||||||
|
];
|
||||||
|
}
|
|
@ -2,17 +2,23 @@
|
||||||
/**
|
/**
|
||||||
* Cookie storage object
|
* Cookie storage object
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Cookies
|
||||||
* @subpackage Cookies
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Iri;
|
||||||
|
use WpOrg\Requests\Response\Headers;
|
||||||
|
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cookie storage object
|
* Cookie storage object
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Cookies
|
||||||
* @subpackage Cookies
|
|
||||||
*/
|
*/
|
||||||
class Requests_Cookie {
|
class Cookie {
|
||||||
/**
|
/**
|
||||||
* Cookie name.
|
* Cookie name.
|
||||||
*
|
*
|
||||||
|
@ -33,9 +39,9 @@ class Requests_Cookie {
|
||||||
* Valid keys are (currently) path, domain, expires, max-age, secure and
|
* Valid keys are (currently) path, domain, expires, max-age, secure and
|
||||||
* httponly.
|
* httponly.
|
||||||
*
|
*
|
||||||
* @var Requests_Utility_CaseInsensitiveDictionary|array Array-like object
|
* @var \WpOrg\Requests\Utility\CaseInsensitiveDictionary|array Array-like object
|
||||||
*/
|
*/
|
||||||
public $attributes = array();
|
public $attributes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cookie flags
|
* Cookie flags
|
||||||
|
@ -45,7 +51,7 @@ class Requests_Cookie {
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $flags = array();
|
public $flags = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference time for relative calculations
|
* Reference time for relative calculations
|
||||||
|
@ -62,18 +68,46 @@ class Requests_Cookie {
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $value
|
* @param string $value
|
||||||
* @param array|Requests_Utility_CaseInsensitiveDictionary $attributes Associative array of attribute data
|
* @param array|\WpOrg\Requests\Utility\CaseInsensitiveDictionary $attributes Associative array of attribute data
|
||||||
|
* @param array $flags
|
||||||
|
* @param int|null $reference_time
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $name argument is not a string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $value argument is not a string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $attributes argument is not an array or iterable object with array access.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $flags argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $reference_time argument is not an integer or null.
|
||||||
*/
|
*/
|
||||||
public function __construct($name, $value, $attributes = array(), $flags = array(), $reference_time = null) {
|
public function __construct($name, $value, $attributes = [], $flags = [], $reference_time = null) {
|
||||||
|
if (is_string($name) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$name', 'string', gettype($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$value', 'string', gettype($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InputValidator::has_array_access($attributes) === false || InputValidator::is_iterable($attributes) === false) {
|
||||||
|
throw InvalidArgument::create(3, '$attributes', 'array|ArrayAccess&Traversable', gettype($attributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($flags) === false) {
|
||||||
|
throw InvalidArgument::create(4, '$flags', 'array', gettype($flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reference_time !== null && is_int($reference_time) === false) {
|
||||||
|
throw InvalidArgument::create(5, '$reference_time', 'integer|null', gettype($reference_time));
|
||||||
|
}
|
||||||
|
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
$default_flags = array(
|
$default_flags = [
|
||||||
'creation' => time(),
|
'creation' => time(),
|
||||||
'last-access' => time(),
|
'last-access' => time(),
|
||||||
'persistent' => false,
|
'persistent' => false,
|
||||||
'host-only' => true,
|
'host-only' => true,
|
||||||
);
|
];
|
||||||
$this->flags = array_merge($default_flags, $flags);
|
$this->flags = array_merge($default_flags, $flags);
|
||||||
|
|
||||||
$this->reference_time = time();
|
$this->reference_time = time();
|
||||||
|
@ -84,6 +118,15 @@ class Requests_Cookie {
|
||||||
$this->normalize();
|
$this->normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cookie value
|
||||||
|
*
|
||||||
|
* Attributes and other data can be accessed via methods.
|
||||||
|
*/
|
||||||
|
public function __toString() {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a cookie is expired.
|
* Check if a cookie is expired.
|
||||||
*
|
*
|
||||||
|
@ -113,10 +156,10 @@ class Requests_Cookie {
|
||||||
/**
|
/**
|
||||||
* Check if a cookie is valid for a given URI
|
* Check if a cookie is valid for a given URI
|
||||||
*
|
*
|
||||||
* @param Requests_IRI $uri URI to check
|
* @param \WpOrg\Requests\Iri $uri URI to check
|
||||||
* @return boolean Whether the cookie is valid for the given URI
|
* @return boolean Whether the cookie is valid for the given URI
|
||||||
*/
|
*/
|
||||||
public function uri_matches(Requests_IRI $uri) {
|
public function uri_matches(Iri $uri) {
|
||||||
if (!$this->domain_matches($uri->host)) {
|
if (!$this->domain_matches($uri->host)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -131,19 +174,23 @@ class Requests_Cookie {
|
||||||
/**
|
/**
|
||||||
* Check if a cookie is valid for a given domain
|
* Check if a cookie is valid for a given domain
|
||||||
*
|
*
|
||||||
* @param string $string Domain to check
|
* @param string $domain Domain to check
|
||||||
* @return boolean Whether the cookie is valid for the given domain
|
* @return boolean Whether the cookie is valid for the given domain
|
||||||
*/
|
*/
|
||||||
public function domain_matches($string) {
|
public function domain_matches($domain) {
|
||||||
|
if (is_string($domain) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($this->attributes['domain'])) {
|
if (!isset($this->attributes['domain'])) {
|
||||||
// Cookies created manually; cookies created by Requests will set
|
// Cookies created manually; cookies created by Requests will set
|
||||||
// the domain to the requested domain
|
// the domain to the requested domain
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$domain_string = $this->attributes['domain'];
|
$cookie_domain = $this->attributes['domain'];
|
||||||
if ($domain_string === $string) {
|
if ($cookie_domain === $domain) {
|
||||||
// The domain string and the string are identical.
|
// The cookie domain and the passed domain are identical.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,26 +200,26 @@ class Requests_Cookie {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($string) <= strlen($domain_string)) {
|
if (strlen($domain) <= strlen($cookie_domain)) {
|
||||||
// For obvious reasons, the string cannot be a suffix if the domain
|
// For obvious reasons, the cookie domain cannot be a suffix if the passed domain
|
||||||
// is shorter than the domain string
|
// is shorter than the cookie domain
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substr($string, -1 * strlen($domain_string)) !== $domain_string) {
|
if (substr($domain, -1 * strlen($cookie_domain)) !== $cookie_domain) {
|
||||||
// The domain string should be a suffix of the string.
|
// The cookie domain should be a suffix of the passed domain.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$prefix = substr($string, 0, strlen($string) - strlen($domain_string));
|
$prefix = substr($domain, 0, strlen($domain) - strlen($cookie_domain));
|
||||||
if (substr($prefix, -1) !== '.') {
|
if (substr($prefix, -1) !== '.') {
|
||||||
// The last character of the string that is not included in the
|
// The last character of the passed domain that is not included in the
|
||||||
// domain string should be a %x2E (".") character.
|
// domain string should be a %x2E (".") character.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The string should be a host name (i.e., not an IP address).
|
// The passed domain should be a host name (i.e., not an IP address).
|
||||||
return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $string);
|
return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,6 +242,10 @@ class Requests_Cookie {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_scalar($request_path) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$cookie_path = $this->attributes['path'];
|
$cookie_path = $this->attributes['path'];
|
||||||
|
|
||||||
if ($cookie_path === $request_path) {
|
if ($cookie_path === $request_path) {
|
||||||
|
@ -280,8 +331,7 @@ class Requests_Cookie {
|
||||||
$delta_seconds = (int) $value;
|
$delta_seconds = (int) $value;
|
||||||
if ($delta_seconds <= 0) {
|
if ($delta_seconds <= 0) {
|
||||||
$expiry_time = 0;
|
$expiry_time = 0;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$expiry_time = $this->reference_time + $delta_seconds;
|
$expiry_time = $this->reference_time + $delta_seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,17 +366,6 @@ class Requests_Cookie {
|
||||||
return sprintf('%s=%s', $this->name, $this->value);
|
return sprintf('%s=%s', $this->name, $this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a cookie for a Cookie header
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @deprecated Use {@see Requests_Cookie::format_for_header}
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatForHeader() {
|
|
||||||
return $this->format_for_header();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a cookie for a Set-Cookie header
|
* Format a cookie for a Set-Cookie header
|
||||||
*
|
*
|
||||||
|
@ -338,42 +377,22 @@ class Requests_Cookie {
|
||||||
public function format_for_set_cookie() {
|
public function format_for_set_cookie() {
|
||||||
$header_value = $this->format_for_header();
|
$header_value = $this->format_for_header();
|
||||||
if (!empty($this->attributes)) {
|
if (!empty($this->attributes)) {
|
||||||
$parts = array();
|
$parts = [];
|
||||||
foreach ($this->attributes as $key => $value) {
|
foreach ($this->attributes as $key => $value) {
|
||||||
// Ignore non-associative attributes
|
// Ignore non-associative attributes
|
||||||
if (is_numeric($key)) {
|
if (is_numeric($key)) {
|
||||||
$parts[] = $value;
|
$parts[] = $value;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$parts[] = sprintf('%s=%s', $key, $value);
|
$parts[] = sprintf('%s=%s', $key, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$header_value .= '; ' . implode('; ', $parts);
|
$header_value .= '; ' . implode('; ', $parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $header_value;
|
return $header_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a cookie for a Set-Cookie header
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @deprecated Use {@see Requests_Cookie::format_for_set_cookie}
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function formatForSetCookie() {
|
|
||||||
return $this->format_for_set_cookie();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the cookie value
|
|
||||||
*
|
|
||||||
* Attributes and other data can be accessed via methods.
|
|
||||||
*/
|
|
||||||
public function __toString() {
|
|
||||||
return $this->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a cookie string into a cookie object
|
* Parse a cookie string into a cookie object
|
||||||
*
|
*
|
||||||
|
@ -381,17 +400,29 @@ class Requests_Cookie {
|
||||||
* is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265
|
* is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265
|
||||||
* specifies some of this handling, but not in a thorough manner.
|
* specifies some of this handling, but not in a thorough manner.
|
||||||
*
|
*
|
||||||
* @param string Cookie header value (from a Set-Cookie header)
|
* @param string $cookie_header Cookie header value (from a Set-Cookie header)
|
||||||
* @return Requests_Cookie Parsed cookie object
|
* @param string $name
|
||||||
|
* @param int|null $reference_time
|
||||||
|
* @return \WpOrg\Requests\Cookie Parsed cookie object
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cookie_header argument is not a string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $name argument is not a string.
|
||||||
*/
|
*/
|
||||||
public static function parse($string, $name = '', $reference_time = null) {
|
public static function parse($cookie_header, $name = '', $reference_time = null) {
|
||||||
$parts = explode(';', $string);
|
if (is_string($cookie_header) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$cookie_header', 'string', gettype($cookie_header));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($name) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$name', 'string', gettype($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode(';', $cookie_header);
|
||||||
$kvparts = array_shift($parts);
|
$kvparts = array_shift($parts);
|
||||||
|
|
||||||
if (!empty($name)) {
|
if (!empty($name)) {
|
||||||
$value = $string;
|
$value = $cookie_header;
|
||||||
}
|
} elseif (strpos($kvparts, '=') === false) {
|
||||||
elseif (strpos($kvparts, '=') === false) {
|
|
||||||
// Some sites might only have a value without the equals separator.
|
// Some sites might only have a value without the equals separator.
|
||||||
// Deviate from RFC 6265 and pretend it was actually a blank name
|
// Deviate from RFC 6265 and pretend it was actually a blank name
|
||||||
// (`=foo`)
|
// (`=foo`)
|
||||||
|
@ -399,23 +430,22 @@ class Requests_Cookie {
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=169091
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=169091
|
||||||
$name = '';
|
$name = '';
|
||||||
$value = $kvparts;
|
$value = $kvparts;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
list($name, $value) = explode('=', $kvparts, 2);
|
list($name, $value) = explode('=', $kvparts, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = trim($name);
|
$name = trim($name);
|
||||||
$value = trim($value);
|
$value = trim($value);
|
||||||
|
|
||||||
// Attribute key are handled case-insensitively
|
// Attribute keys are handled case-insensitively
|
||||||
$attributes = new Requests_Utility_CaseInsensitiveDictionary();
|
$attributes = new CaseInsensitiveDictionary();
|
||||||
|
|
||||||
if (!empty($parts)) {
|
if (!empty($parts)) {
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
if (strpos($part, '=') === false) {
|
if (strpos($part, '=') === false) {
|
||||||
$part_key = $part;
|
$part_key = $part;
|
||||||
$part_value = true;
|
$part_value = true;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
list($part_key, $part_value) = explode('=', $part, 2);
|
list($part_key, $part_value) = explode('=', $part, 2);
|
||||||
$part_value = trim($part_value);
|
$part_value = trim($part_value);
|
||||||
}
|
}
|
||||||
|
@ -425,24 +455,24 @@ class Requests_Cookie {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Requests_Cookie($name, $value, $attributes, array(), $reference_time);
|
return new static($name, $value, $attributes, [], $reference_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse all Set-Cookie headers from request headers
|
* Parse all Set-Cookie headers from request headers
|
||||||
*
|
*
|
||||||
* @param Requests_Response_Headers $headers Headers to parse from
|
* @param \WpOrg\Requests\Response\Headers $headers Headers to parse from
|
||||||
* @param Requests_IRI|null $origin URI for comparing cookie origins
|
* @param \WpOrg\Requests\Iri|null $origin URI for comparing cookie origins
|
||||||
* @param int|null $time Reference time for expiration calculation
|
* @param int|null $time Reference time for expiration calculation
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function parse_from_headers(Requests_Response_Headers $headers, Requests_IRI $origin = null, $time = null) {
|
public static function parse_from_headers(Headers $headers, Iri $origin = null, $time = null) {
|
||||||
$cookie_headers = $headers->getValues('Set-Cookie');
|
$cookie_headers = $headers->getValues('Set-Cookie');
|
||||||
if (empty($cookie_headers)) {
|
if (empty($cookie_headers)) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$cookies = array();
|
$cookies = [];
|
||||||
foreach ($cookie_headers as $header) {
|
foreach ($cookie_headers as $header) {
|
||||||
$parsed = self::parse($header, '', $time);
|
$parsed = self::parse($header, '', $time);
|
||||||
|
|
||||||
|
@ -450,8 +480,7 @@ class Requests_Cookie {
|
||||||
if (empty($parsed->attributes['domain']) && !empty($origin)) {
|
if (empty($parsed->attributes['domain']) && !empty($origin)) {
|
||||||
$parsed->attributes['domain'] = $origin->host;
|
$parsed->attributes['domain'] = $origin->host;
|
||||||
$parsed->flags['host-only'] = true;
|
$parsed->flags['host-only'] = true;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$parsed->flags['host-only'] = false;
|
$parsed->flags['host-only'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,19 +494,18 @@ class Requests_Cookie {
|
||||||
// the uri-path is not a %x2F ("/") character, output
|
// the uri-path is not a %x2F ("/") character, output
|
||||||
// %x2F ("/") and skip the remaining steps.
|
// %x2F ("/") and skip the remaining steps.
|
||||||
$path = '/';
|
$path = '/';
|
||||||
}
|
} elseif (substr_count($path, '/') === 1) {
|
||||||
elseif (substr_count($path, '/') === 1) {
|
|
||||||
// If the uri-path contains no more than one %x2F ("/")
|
// If the uri-path contains no more than one %x2F ("/")
|
||||||
// character, output %x2F ("/") and skip the remaining
|
// character, output %x2F ("/") and skip the remaining
|
||||||
// step.
|
// step.
|
||||||
$path = '/';
|
$path = '/';
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Output the characters of the uri-path from the first
|
// Output the characters of the uri-path from the first
|
||||||
// character up to, but not including, the right-most
|
// character up to, but not including, the right-most
|
||||||
// %x2F ("/").
|
// %x2F ("/").
|
||||||
$path = substr($path, 0, strrpos($path, '/'));
|
$path = substr($path, 0, strrpos($path, '/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsed->attributes['path'] = $path;
|
$parsed->attributes['path'] = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,15 +519,4 @@ class Requests_Cookie {
|
||||||
|
|
||||||
return $cookies;
|
return $cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse all Set-Cookie headers from request headers
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @deprecated Use {@see Requests_Cookie::parse_from_headers}
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function parseFromHeaders(Requests_Response_Headers $headers) {
|
|
||||||
return self::parse_from_headers($headers);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Cookie holder object
|
||||||
|
*
|
||||||
|
* @package Requests\Cookies
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Cookie;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use ArrayIterator;
|
||||||
|
use IteratorAggregate;
|
||||||
|
use ReturnTypeWillChange;
|
||||||
|
use WpOrg\Requests\Cookie;
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\HookManager;
|
||||||
|
use WpOrg\Requests\Iri;
|
||||||
|
use WpOrg\Requests\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cookie holder object
|
||||||
|
*
|
||||||
|
* @package Requests\Cookies
|
||||||
|
*/
|
||||||
|
class Jar implements ArrayAccess, IteratorAggregate {
|
||||||
|
/**
|
||||||
|
* Actual item data
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $cookies = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new jar
|
||||||
|
*
|
||||||
|
* @param array $cookies Existing cookie values
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array.
|
||||||
|
*/
|
||||||
|
public function __construct($cookies = []) {
|
||||||
|
if (is_array($cookies) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$cookies', 'array', gettype($cookies));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cookies = $cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalise cookie data into a \WpOrg\Requests\Cookie
|
||||||
|
*
|
||||||
|
* @param string|\WpOrg\Requests\Cookie $cookie
|
||||||
|
* @return \WpOrg\Requests\Cookie
|
||||||
|
*/
|
||||||
|
public function normalize_cookie($cookie, $key = '') {
|
||||||
|
if ($cookie instanceof Cookie) {
|
||||||
|
return $cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Cookie::parse($cookie, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given item exists
|
||||||
|
*
|
||||||
|
* @param string $offset Item key
|
||||||
|
* @return boolean Does the item exist?
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetExists($offset) {
|
||||||
|
return isset($this->cookies[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value for the item
|
||||||
|
*
|
||||||
|
* @param string $offset Item key
|
||||||
|
* @return string|null Item value (null if offsetExists is false)
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetGet($offset) {
|
||||||
|
if (!isset($this->cookies[$offset])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cookies[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the given item
|
||||||
|
*
|
||||||
|
* @param string $offset Item name
|
||||||
|
* @param string $value Item value
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetSet($offset, $value) {
|
||||||
|
if ($offset === null) {
|
||||||
|
throw new Exception('Object is a dictionary, not a list', 'invalidset');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cookies[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset the given header
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetUnset($offset) {
|
||||||
|
unset($this->cookies[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator for the data
|
||||||
|
*
|
||||||
|
* @return \ArrayIterator
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function getIterator() {
|
||||||
|
return new ArrayIterator($this->cookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the cookie handler with the request's hooking system
|
||||||
|
*
|
||||||
|
* @param \WpOrg\Requests\HookManager $hooks Hooking system
|
||||||
|
*/
|
||||||
|
public function register(HookManager $hooks) {
|
||||||
|
$hooks->register('requests.before_request', [$this, 'before_request']);
|
||||||
|
$hooks->register('requests.before_redirect_check', [$this, 'before_redirect_check']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Cookie header to a request if we have any
|
||||||
|
*
|
||||||
|
* As per RFC 6265, cookies are separated by '; '
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @param array $headers
|
||||||
|
* @param array $data
|
||||||
|
* @param string $type
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function before_request($url, &$headers, &$data, &$type, &$options) {
|
||||||
|
if (!$url instanceof Iri) {
|
||||||
|
$url = new Iri($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->cookies)) {
|
||||||
|
$cookies = [];
|
||||||
|
foreach ($this->cookies as $key => $cookie) {
|
||||||
|
$cookie = $this->normalize_cookie($cookie, $key);
|
||||||
|
|
||||||
|
// Skip expired cookies
|
||||||
|
if ($cookie->is_expired()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cookie->domain_matches($url->host)) {
|
||||||
|
$cookies[] = $cookie->format_for_header();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers['Cookie'] = implode('; ', $cookies);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse all cookies from a response and attach them to the response
|
||||||
|
*
|
||||||
|
* @param \WpOrg\Requests\Response $response
|
||||||
|
*/
|
||||||
|
public function before_redirect_check(Response $response) {
|
||||||
|
$url = $response->url;
|
||||||
|
if (!$url instanceof Iri) {
|
||||||
|
$url = new Iri($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cookies = Cookie::parse_from_headers($response->headers, $url);
|
||||||
|
$this->cookies = array_merge($this->cookies, $cookies);
|
||||||
|
$response->cookies = $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for HTTP requests
|
* Exception for HTTP requests
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use Exception as PHPException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for HTTP requests
|
* Exception for HTTP requests
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception extends Exception {
|
class Exception extends PHPException {
|
||||||
/**
|
/**
|
||||||
* Type of exception
|
* Type of exception
|
||||||
*
|
*
|
||||||
|
@ -41,7 +45,7 @@ class Requests_Exception extends Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like {@see getCode()}, but a string code.
|
* Like {@see \Exception::getCode()}, but a string code.
|
||||||
*
|
*
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return string
|
* @return string
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception for when an incorrect number of arguments are passed to a method.
|
||||||
|
*
|
||||||
|
* Typically, this exception is used when all arguments for a method are optional,
|
||||||
|
* but certain arguments need to be passed together, i.e. a method which can be called
|
||||||
|
* with no arguments or with two arguments, but not with one argument.
|
||||||
|
*
|
||||||
|
* Along the same lines, this exception is also used if a method expects an array
|
||||||
|
* with a certain number of elements and the provided number of elements does not comply.
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
final class ArgumentCount extends Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new argument count exception with a standardized text.
|
||||||
|
*
|
||||||
|
* @param string $expected The argument count expected as a phrase.
|
||||||
|
* For example: `at least 2 arguments` or `exactly 1 argument`.
|
||||||
|
* @param int $received The actual argument count received.
|
||||||
|
* @param string $type Exception type.
|
||||||
|
*
|
||||||
|
* @return \WpOrg\Requests\Exception\ArgumentCount
|
||||||
|
*/
|
||||||
|
public static function create($expected, $received, $type) {
|
||||||
|
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
|
||||||
|
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||||
|
|
||||||
|
return new self(
|
||||||
|
sprintf(
|
||||||
|
'%s::%s() expects %s, %d given',
|
||||||
|
$stack[1]['class'],
|
||||||
|
$stack[1]['function'],
|
||||||
|
$expected,
|
||||||
|
$received
|
||||||
|
),
|
||||||
|
$type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,20 @@
|
||||||
/**
|
/**
|
||||||
* Exception based on HTTP response
|
* Exception based on HTTP response
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\Http\StatusUnknown;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception based on HTTP response
|
* Exception based on HTTP response
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP extends Requests_Exception {
|
class Http extends Exception {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
||||||
|
@ -44,7 +49,9 @@ class Requests_Exception_HTTP extends Requests_Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the status message
|
* Get the status message.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getReason() {
|
public function getReason() {
|
||||||
return $this->reason;
|
return $this->reason;
|
||||||
|
@ -58,14 +65,14 @@ class Requests_Exception_HTTP extends Requests_Exception {
|
||||||
*/
|
*/
|
||||||
public static function get_class($code) {
|
public static function get_class($code) {
|
||||||
if (!$code) {
|
if (!$code) {
|
||||||
return 'Requests_Exception_HTTP_Unknown';
|
return StatusUnknown::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
$class = sprintf('Requests_Exception_HTTP_%d', $code);
|
$class = sprintf('\WpOrg\Requests\Exception\Http\Status%d', $code);
|
||||||
if (class_exists($class)) {
|
if (class_exists($class)) {
|
||||||
return $class;
|
return $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Requests_Exception_HTTP_Unknown';
|
return StatusUnknown::class;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 304 Not Modified responses
|
* Exception for 304 Not Modified responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 304 Not Modified responses
|
* Exception for 304 Not Modified responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_304 extends Requests_Exception_HTTP {
|
final class Status304 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 305 Use Proxy responses
|
* Exception for 305 Use Proxy responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 305 Use Proxy responses
|
* Exception for 305 Use Proxy responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_305 extends Requests_Exception_HTTP {
|
final class Status305 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 306 Switch Proxy responses
|
* Exception for 306 Switch Proxy responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 306 Switch Proxy responses
|
* Exception for 306 Switch Proxy responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_306 extends Requests_Exception_HTTP {
|
final class Status306 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 400 Bad Request responses
|
* Exception for 400 Bad Request responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 400 Bad Request responses
|
* Exception for 400 Bad Request responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_400 extends Requests_Exception_HTTP {
|
final class Status400 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 401 Unauthorized responses
|
* Exception for 401 Unauthorized responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 401 Unauthorized responses
|
* Exception for 401 Unauthorized responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_401 extends Requests_Exception_HTTP {
|
final class Status401 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 402 Payment Required responses
|
* Exception for 402 Payment Required responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 402 Payment Required responses
|
* Exception for 402 Payment Required responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_402 extends Requests_Exception_HTTP {
|
final class Status402 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 403 Forbidden responses
|
* Exception for 403 Forbidden responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 403 Forbidden responses
|
* Exception for 403 Forbidden responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_403 extends Requests_Exception_HTTP {
|
final class Status403 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 404 Not Found responses
|
* Exception for 404 Not Found responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 404 Not Found responses
|
* Exception for 404 Not Found responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_404 extends Requests_Exception_HTTP {
|
final class Status404 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 405 Method Not Allowed responses
|
* Exception for 405 Method Not Allowed responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 405 Method Not Allowed responses
|
* Exception for 405 Method Not Allowed responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_405 extends Requests_Exception_HTTP {
|
final class Status405 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 406 Not Acceptable responses
|
* Exception for 406 Not Acceptable responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 406 Not Acceptable responses
|
* Exception for 406 Not Acceptable responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_406 extends Requests_Exception_HTTP {
|
final class Status406 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 407 Proxy Authentication Required responses
|
* Exception for 407 Proxy Authentication Required responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 407 Proxy Authentication Required responses
|
* Exception for 407 Proxy Authentication Required responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_407 extends Requests_Exception_HTTP {
|
final class Status407 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 408 Request Timeout responses
|
* Exception for 408 Request Timeout responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 408 Request Timeout responses
|
* Exception for 408 Request Timeout responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_408 extends Requests_Exception_HTTP {
|
final class Status408 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 409 Conflict responses
|
* Exception for 409 Conflict responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 409 Conflict responses
|
* Exception for 409 Conflict responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_409 extends Requests_Exception_HTTP {
|
final class Status409 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 410 Gone responses
|
* Exception for 410 Gone responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 410 Gone responses
|
* Exception for 410 Gone responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_410 extends Requests_Exception_HTTP {
|
final class Status410 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 411 Length Required responses
|
* Exception for 411 Length Required responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 411 Length Required responses
|
* Exception for 411 Length Required responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_411 extends Requests_Exception_HTTP {
|
final class Status411 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 412 Precondition Failed responses
|
* Exception for 412 Precondition Failed responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 412 Precondition Failed responses
|
* Exception for 412 Precondition Failed responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_412 extends Requests_Exception_HTTP {
|
final class Status412 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 413 Request Entity Too Large responses
|
* Exception for 413 Request Entity Too Large responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 413 Request Entity Too Large responses
|
* Exception for 413 Request Entity Too Large responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_413 extends Requests_Exception_HTTP {
|
final class Status413 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 414 Request-URI Too Large responses
|
* Exception for 414 Request-URI Too Large responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 414 Request-URI Too Large responses
|
* Exception for 414 Request-URI Too Large responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_414 extends Requests_Exception_HTTP {
|
final class Status414 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 415 Unsupported Media Type responses
|
* Exception for 415 Unsupported Media Type responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 415 Unsupported Media Type responses
|
* Exception for 415 Unsupported Media Type responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_415 extends Requests_Exception_HTTP {
|
final class Status415 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 416 Requested Range Not Satisfiable responses
|
* Exception for 416 Requested Range Not Satisfiable responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 416 Requested Range Not Satisfiable responses
|
* Exception for 416 Requested Range Not Satisfiable responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_416 extends Requests_Exception_HTTP {
|
final class Status416 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 417 Expectation Failed responses
|
* Exception for 417 Expectation Failed responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 417 Expectation Failed responses
|
* Exception for 417 Expectation Failed responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_417 extends Requests_Exception_HTTP {
|
final class Status417 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,17 +2,23 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 418 I'm A Teapot responses
|
* Exception for 418 I'm A Teapot responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc2324
|
* @link https://tools.ietf.org/html/rfc2324
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 418 I'm A Teapot responses
|
* Exception for 418 I'm A Teapot responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc2324
|
* @link https://tools.ietf.org/html/rfc2324
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_418 extends Requests_Exception_HTTP {
|
final class Status418 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,17 +2,23 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 428 Precondition Required responses
|
* Exception for 428 Precondition Required responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6585
|
* @link https://tools.ietf.org/html/rfc6585
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 428 Precondition Required responses
|
* Exception for 428 Precondition Required responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6585
|
* @link https://tools.ietf.org/html/rfc6585
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_428 extends Requests_Exception_HTTP {
|
final class Status428 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Exception for 429 Too Many Requests responses
|
||||||
|
*
|
||||||
|
* @link https://tools.ietf.org/html/draft-nottingham-http-new-status-04
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception for 429 Too Many Requests responses
|
||||||
|
*
|
||||||
|
* @link https://tools.ietf.org/html/draft-nottingham-http-new-status-04
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
*/
|
||||||
|
final class Status429 extends Http {
|
||||||
|
/**
|
||||||
|
* HTTP status code
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $code = 429;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reason phrase
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $reason = 'Too Many Requests';
|
||||||
|
}
|
|
@ -2,17 +2,23 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 431 Request Header Fields Too Large responses
|
* Exception for 431 Request Header Fields Too Large responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6585
|
* @link https://tools.ietf.org/html/rfc6585
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 431 Request Header Fields Too Large responses
|
* Exception for 431 Request Header Fields Too Large responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6585
|
* @link https://tools.ietf.org/html/rfc6585
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_431 extends Requests_Exception_HTTP {
|
final class Status431 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 500 Internal Server Error responses
|
* Exception for 500 Internal Server Error responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 500 Internal Server Error responses
|
* Exception for 500 Internal Server Error responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_500 extends Requests_Exception_HTTP {
|
final class Status500 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 501 Not Implemented responses
|
* Exception for 501 Not Implemented responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 501 Not Implemented responses
|
* Exception for 501 Not Implemented responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_501 extends Requests_Exception_HTTP {
|
final class Status501 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 502 Bad Gateway responses
|
* Exception for 502 Bad Gateway responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 502 Bad Gateway responses
|
* Exception for 502 Bad Gateway responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_502 extends Requests_Exception_HTTP {
|
final class Status502 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 503 Service Unavailable responses
|
* Exception for 503 Service Unavailable responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 503 Service Unavailable responses
|
* Exception for 503 Service Unavailable responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_503 extends Requests_Exception_HTTP {
|
final class Status503 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 504 Gateway Timeout responses
|
* Exception for 504 Gateway Timeout responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 504 Gateway Timeout responses
|
* Exception for 504 Gateway Timeout responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_504 extends Requests_Exception_HTTP {
|
final class Status504 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 505 HTTP Version Not Supported responses
|
* Exception for 505 HTTP Version Not Supported responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 505 HTTP Version Not Supported responses
|
* Exception for 505 HTTP Version Not Supported responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_505 extends Requests_Exception_HTTP {
|
final class Status505 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,17 +2,23 @@
|
||||||
/**
|
/**
|
||||||
* Exception for 511 Network Authentication Required responses
|
* Exception for 511 Network Authentication Required responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6585
|
* @link https://tools.ietf.org/html/rfc6585
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for 511 Network Authentication Required responses
|
* Exception for 511 Network Authentication Required responses
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc6585
|
* @link https://tools.ietf.org/html/rfc6585
|
||||||
* @package Requests
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_511 extends Requests_Exception_HTTP {
|
final class Status511 extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
|
@ -2,15 +2,20 @@
|
||||||
/**
|
/**
|
||||||
* Exception for unknown status responses
|
* Exception for unknown status responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception\Http;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
use WpOrg\Requests\Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception for unknown status responses
|
* Exception for unknown status responses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Exceptions
|
||||||
*/
|
*/
|
||||||
class Requests_Exception_HTTP_Unknown extends Requests_Exception_HTTP {
|
final class StatusUnknown extends Http {
|
||||||
/**
|
/**
|
||||||
* HTTP status code
|
* HTTP status code
|
||||||
*
|
*
|
||||||
|
@ -28,15 +33,15 @@ class Requests_Exception_HTTP_Unknown extends Requests_Exception_HTTP {
|
||||||
/**
|
/**
|
||||||
* Create a new exception
|
* Create a new exception
|
||||||
*
|
*
|
||||||
* If `$data` is an instance of {@see Requests_Response}, uses the status
|
* If `$data` is an instance of {@see \WpOrg\Requests\Response}, uses the status
|
||||||
* code from it. Otherwise, sets as 0
|
* code from it. Otherwise, sets as 0
|
||||||
*
|
*
|
||||||
* @param string|null $reason Reason phrase
|
* @param string|null $reason Reason phrase
|
||||||
* @param mixed $data Associated data
|
* @param mixed $data Associated data
|
||||||
*/
|
*/
|
||||||
public function __construct($reason = null, $data = null) {
|
public function __construct($reason = null, $data = null) {
|
||||||
if ($data instanceof Requests_Response) {
|
if ($data instanceof Response) {
|
||||||
$this->code = $data->status_code;
|
$this->code = (int) $data->status_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::__construct($reason, $data);
|
parent::__construct($reason, $data);
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception for an invalid argument passed.
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
final class InvalidArgument extends InvalidArgumentException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new invalid argument exception with a standardized text.
|
||||||
|
*
|
||||||
|
* @param int $position The argument position in the function signature. 1-based.
|
||||||
|
* @param string $name The argument name in the function signature.
|
||||||
|
* @param string $expected The argument type expected as a string.
|
||||||
|
* @param string $received The actual argument type received.
|
||||||
|
*
|
||||||
|
* @return \WpOrg\Requests\Exception\InvalidArgument
|
||||||
|
*/
|
||||||
|
public static function create($position, $name, $expected, $received) {
|
||||||
|
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
|
||||||
|
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||||
|
|
||||||
|
return new self(
|
||||||
|
sprintf(
|
||||||
|
'%s::%s(): Argument #%d (%s) must be of type %s, %s given',
|
||||||
|
$stack[1]['class'],
|
||||||
|
$stack[1]['function'],
|
||||||
|
$position,
|
||||||
|
$name,
|
||||||
|
$expected,
|
||||||
|
$received
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Transport Exception
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transport Exception
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
*/
|
||||||
|
class Transport extends Exception {}
|
|
@ -1,6 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* CURL Transport Exception.
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
*/
|
||||||
|
|
||||||
class Requests_Exception_Transport_cURL extends Requests_Exception_Transport {
|
namespace WpOrg\Requests\Exception\Transport;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CURL Transport Exception.
|
||||||
|
*
|
||||||
|
* @package Requests\Exceptions
|
||||||
|
*/
|
||||||
|
final class Curl extends Transport {
|
||||||
|
|
||||||
const EASY = 'cURLEasy';
|
const EASY = 'cURLEasy';
|
||||||
const MULTI = 'cURLMulti';
|
const MULTI = 'cURLMulti';
|
||||||
|
@ -29,13 +43,21 @@ class Requests_Exception_Transport_cURL extends Requests_Exception_Transport {
|
||||||
*/
|
*/
|
||||||
protected $reason = 'Unknown';
|
protected $reason = 'Unknown';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new exception.
|
||||||
|
*
|
||||||
|
* @param string $message Exception message.
|
||||||
|
* @param string $type Exception type.
|
||||||
|
* @param mixed $data Associated data, if applicable.
|
||||||
|
* @param int $code Exception numerical code, if applicable.
|
||||||
|
*/
|
||||||
public function __construct($message, $type, $data = null, $code = 0) {
|
public function __construct($message, $type, $data = null, $code = 0) {
|
||||||
if ($type !== null) {
|
if ($type !== null) {
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($code !== null) {
|
if ($code !== null) {
|
||||||
$this->code = $code;
|
$this->code = (int) $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($message !== null) {
|
if ($message !== null) {
|
||||||
|
@ -47,7 +69,9 @@ class Requests_Exception_Transport_cURL extends Requests_Exception_Transport {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the error message
|
* Get the error message.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getReason() {
|
public function getReason() {
|
||||||
return $this->reason;
|
return $this->reason;
|
|
@ -2,22 +2,22 @@
|
||||||
/**
|
/**
|
||||||
* Event dispatcher
|
* Event dispatcher
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\EventDispatcher
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event dispatcher
|
* Event dispatcher
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\EventDispatcher
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
interface Requests_Hooker {
|
interface HookManager {
|
||||||
/**
|
/**
|
||||||
* Register a callback for a hook
|
* Register a callback for a hook
|
||||||
*
|
*
|
||||||
* @param string $hook Hook name
|
* @param string $hook Hook name
|
||||||
* @param callback $callback Function/method to call on event
|
* @param callable $callback Function/method to call on event
|
||||||
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
||||||
*/
|
*/
|
||||||
public function register($hook, $callback, $priority = 0);
|
public function register($hook, $callback, $priority = 0);
|
||||||
|
@ -29,5 +29,5 @@ interface Requests_Hooker {
|
||||||
* @param array $parameters Parameters to pass to callbacks
|
* @param array $parameters Parameters to pass to callbacks
|
||||||
* @return boolean Successfulness
|
* @return boolean Successfulness
|
||||||
*/
|
*/
|
||||||
public function dispatch($hook, $parameters = array());
|
public function dispatch($hook, $parameters = []);
|
||||||
}
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Handles adding and dispatching events
|
||||||
|
*
|
||||||
|
* @package Requests\EventDispatcher
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\HookManager;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles adding and dispatching events
|
||||||
|
*
|
||||||
|
* @package Requests\EventDispatcher
|
||||||
|
*/
|
||||||
|
class Hooks implements HookManager {
|
||||||
|
/**
|
||||||
|
* Registered callbacks for each hook
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $hooks = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback for a hook
|
||||||
|
*
|
||||||
|
* @param string $hook Hook name
|
||||||
|
* @param callable $callback Function/method to call on event
|
||||||
|
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $hook argument is not a string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $callback argument is not callable.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $priority argument is not an integer.
|
||||||
|
*/
|
||||||
|
public function register($hook, $callback, $priority = 0) {
|
||||||
|
if (is_string($hook) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$hook', 'string', gettype($hook));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_callable($callback) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$callback', 'callable', gettype($callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InputValidator::is_numeric_array_key($priority) === false) {
|
||||||
|
throw InvalidArgument::create(3, '$priority', 'integer', gettype($priority));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->hooks[$hook])) {
|
||||||
|
$this->hooks[$hook] = [
|
||||||
|
$priority => [],
|
||||||
|
];
|
||||||
|
} elseif (!isset($this->hooks[$hook][$priority])) {
|
||||||
|
$this->hooks[$hook][$priority] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->hooks[$hook][$priority][] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a message
|
||||||
|
*
|
||||||
|
* @param string $hook Hook name
|
||||||
|
* @param array $parameters Parameters to pass to callbacks
|
||||||
|
* @return boolean Successfulness
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $hook argument is not a string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $parameters argument is not an array.
|
||||||
|
*/
|
||||||
|
public function dispatch($hook, $parameters = []) {
|
||||||
|
if (is_string($hook) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$hook', 'string', gettype($hook));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check strictly against array, as Array* objects don't work in combination with `call_user_func_array()`.
|
||||||
|
if (is_array($parameters) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$parameters', 'array', gettype($parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($this->hooks[$hook])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($parameters)) {
|
||||||
|
// Strip potential keys from the array to prevent them being interpreted as parameter names in PHP 8.0.
|
||||||
|
$parameters = array_values($parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($this->hooks[$hook]);
|
||||||
|
|
||||||
|
foreach ($this->hooks[$hook] as $priority => $hooked) {
|
||||||
|
foreach ($hooked as $callback) {
|
||||||
|
$callback(...$parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +1,45 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IDNA URL encoder
|
* IDNA URL encoder
|
||||||
*
|
*
|
||||||
* Note: Not fully compliant, as nameprep does nothing yet.
|
* Note: Not fully compliant, as nameprep does nothing yet.
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc3490 IDNA specification
|
* @link https://tools.ietf.org/html/rfc3490 IDNA specification
|
||||||
* @see https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification
|
* @link https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification
|
||||||
*/
|
*/
|
||||||
class Requests_IDNAEncoder {
|
class IdnaEncoder {
|
||||||
/**
|
/**
|
||||||
* ACE prefix used for IDNA
|
* ACE prefix used for IDNA
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc3490#section-5
|
* @link https://tools.ietf.org/html/rfc3490#section-5
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
const ACE_PREFIX = 'xn--';
|
const ACE_PREFIX = 'xn--';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum length of a IDNA URL in ASCII.
|
||||||
|
*
|
||||||
|
* @see \WpOrg\Requests\IdnaEncoder::to_ascii()
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const MAX_LENGTH = 64;
|
||||||
|
|
||||||
/**#@+
|
/**#@+
|
||||||
* Bootstrap constant for Punycode
|
* Bootstrap constant for Punycode
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc3492#section-5
|
* @link https://tools.ietf.org/html/rfc3492#section-5
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
const BOOTSTRAP_BASE = 36;
|
const BOOTSTRAP_BASE = 36;
|
||||||
|
@ -37,106 +54,119 @@ class Requests_IDNAEncoder {
|
||||||
/**
|
/**
|
||||||
* Encode a hostname using Punycode
|
* Encode a hostname using Punycode
|
||||||
*
|
*
|
||||||
* @param string $string Hostname
|
* @param string|Stringable $hostname Hostname
|
||||||
* @return string Punycode-encoded hostname
|
* @return string Punycode-encoded hostname
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
|
||||||
*/
|
*/
|
||||||
public static function encode($string) {
|
public static function encode($hostname) {
|
||||||
$parts = explode('.', $string);
|
if (InputValidator::is_string_or_stringable($hostname) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$hostname', 'string|Stringable', gettype($hostname));
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode('.', $hostname);
|
||||||
foreach ($parts as &$part) {
|
foreach ($parts as &$part) {
|
||||||
$part = self::to_ascii($part);
|
$part = self::to_ascii($part);
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode('.', $parts);
|
return implode('.', $parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a UTF-8 string to an ASCII string using Punycode
|
* Convert a UTF-8 text string to an ASCII string using Punycode
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`)
|
* @param string $text ASCII or UTF-8 string (max length 64 characters)
|
||||||
* @throws Requests_Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`)
|
|
||||||
* @throws Requests_Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`)
|
|
||||||
* @throws Requests_Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`)
|
|
||||||
*
|
|
||||||
* @param string $string ASCII or UTF-8 string (max length 64 characters)
|
|
||||||
* @return string ASCII string
|
* @return string ASCII string
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`)
|
||||||
|
* @throws \WpOrg\Requests\Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`)
|
||||||
|
* @throws \WpOrg\Requests\Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`)
|
||||||
|
* @throws \WpOrg\Requests\Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`)
|
||||||
*/
|
*/
|
||||||
public static function to_ascii($string) {
|
public static function to_ascii($text) {
|
||||||
// Step 1: Check if the string is already ASCII
|
// Step 1: Check if the text is already ASCII
|
||||||
if (self::is_ascii($string)) {
|
if (self::is_ascii($text)) {
|
||||||
// Skip to step 7
|
// Skip to step 7
|
||||||
if (strlen($string) < 64) {
|
if (strlen($text) < self::MAX_LENGTH) {
|
||||||
return $string;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Requests_Exception('Provided string is too long', 'idna.provided_too_long', $string);
|
throw new Exception('Provided string is too long', 'idna.provided_too_long', $text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: nameprep
|
// Step 2: nameprep
|
||||||
$string = self::nameprep($string);
|
$text = self::nameprep($text);
|
||||||
|
|
||||||
// Step 3: UseSTD3ASCIIRules is false, continue
|
// Step 3: UseSTD3ASCIIRules is false, continue
|
||||||
// Step 4: Check if it's ASCII now
|
// Step 4: Check if it's ASCII now
|
||||||
if (self::is_ascii($string)) {
|
if (self::is_ascii($text)) {
|
||||||
// Skip to step 7
|
// Skip to step 7
|
||||||
if (strlen($string) < 64) {
|
/*
|
||||||
return $string;
|
* As the `nameprep()` method returns the original string, this code will never be reached until
|
||||||
|
* that method is properly implemented.
|
||||||
|
*/
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
if (strlen($text) < self::MAX_LENGTH) {
|
||||||
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Requests_Exception('Prepared string is too long', 'idna.prepared_too_long', $string);
|
throw new Exception('Prepared string is too long', 'idna.prepared_too_long', $text);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Check ACE prefix
|
// Step 5: Check ACE prefix
|
||||||
if (strpos($string, self::ACE_PREFIX) === 0) {
|
if (strpos($text, self::ACE_PREFIX) === 0) {
|
||||||
throw new Requests_Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $string);
|
throw new Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: Encode with Punycode
|
// Step 6: Encode with Punycode
|
||||||
$string = self::punycode_encode($string);
|
$text = self::punycode_encode($text);
|
||||||
|
|
||||||
// Step 7: Prepend ACE prefix
|
// Step 7: Prepend ACE prefix
|
||||||
$string = self::ACE_PREFIX . $string;
|
$text = self::ACE_PREFIX . $text;
|
||||||
|
|
||||||
// Step 8: Check size
|
// Step 8: Check size
|
||||||
if (strlen($string) < 64) {
|
if (strlen($text) < self::MAX_LENGTH) {
|
||||||
return $string;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Requests_Exception('Encoded string is too long', 'idna.encoded_too_long', $string);
|
throw new Exception('Encoded string is too long', 'idna.encoded_too_long', $text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a given string contains only ASCII characters
|
* Check whether a given text string contains only ASCII characters
|
||||||
*
|
*
|
||||||
* @internal (Testing found regex was the fastest implementation)
|
* @internal (Testing found regex was the fastest implementation)
|
||||||
*
|
*
|
||||||
* @param string $string
|
* @param string $text
|
||||||
* @return bool Is the string ASCII-only?
|
* @return bool Is the text string ASCII-only?
|
||||||
*/
|
*/
|
||||||
protected static function is_ascii($string) {
|
protected static function is_ascii($text) {
|
||||||
return (preg_match('/(?:[^\x00-\x7F])/', $string) !== 1);
|
return (preg_match('/(?:[^\x00-\x7F])/', $text) !== 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare a string for use as an IDNA name
|
* Prepare a text string for use as an IDNA name
|
||||||
*
|
*
|
||||||
* @todo Implement this based on RFC 3491 and the newer 5891
|
* @todo Implement this based on RFC 3491 and the newer 5891
|
||||||
* @param string $string
|
* @param string $text
|
||||||
* @return string Prepared string
|
* @return string Prepared string
|
||||||
*/
|
*/
|
||||||
protected static function nameprep($string) {
|
protected static function nameprep($text) {
|
||||||
return $string;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a UTF-8 string to a UCS-4 codepoint array
|
* Convert a UTF-8 string to a UCS-4 codepoint array
|
||||||
*
|
*
|
||||||
* Based on Requests_IRI::replace_invalid_with_pct_encoding()
|
* Based on \WpOrg\Requests\Iri::replace_invalid_with_pct_encoding()
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`)
|
|
||||||
* @param string $input
|
* @param string $input
|
||||||
* @return array Unicode code points
|
* @return array Unicode code points
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`)
|
||||||
*/
|
*/
|
||||||
protected static function utf8_to_codepoints($input) {
|
protected static function utf8_to_codepoints($input) {
|
||||||
$codepoints = array();
|
$codepoints = [];
|
||||||
|
|
||||||
// Get number of bytes
|
// Get number of bytes
|
||||||
$strlen = strlen($input);
|
$strlen = strlen($input);
|
||||||
|
@ -145,50 +175,43 @@ class Requests_IDNAEncoder {
|
||||||
for ($position = 0; $position < $strlen; $position++) {
|
for ($position = 0; $position < $strlen; $position++) {
|
||||||
$value = ord($input[$position]);
|
$value = ord($input[$position]);
|
||||||
|
|
||||||
// One byte sequence:
|
if ((~$value & 0x80) === 0x80) { // One byte sequence:
|
||||||
if ((~$value & 0x80) === 0x80) {
|
|
||||||
$character = $value;
|
$character = $value;
|
||||||
$length = 1;
|
$length = 1;
|
||||||
$remaining = 0;
|
$remaining = 0;
|
||||||
}
|
} elseif (($value & 0xE0) === 0xC0) { // Two byte sequence:
|
||||||
// Two byte sequence:
|
|
||||||
elseif (($value & 0xE0) === 0xC0) {
|
|
||||||
$character = ($value & 0x1F) << 6;
|
$character = ($value & 0x1F) << 6;
|
||||||
$length = 2;
|
$length = 2;
|
||||||
$remaining = 1;
|
$remaining = 1;
|
||||||
}
|
} elseif (($value & 0xF0) === 0xE0) { // Three byte sequence:
|
||||||
// Three byte sequence:
|
|
||||||
elseif (($value & 0xF0) === 0xE0) {
|
|
||||||
$character = ($value & 0x0F) << 12;
|
$character = ($value & 0x0F) << 12;
|
||||||
$length = 3;
|
$length = 3;
|
||||||
$remaining = 2;
|
$remaining = 2;
|
||||||
}
|
} elseif (($value & 0xF8) === 0xF0) { // Four byte sequence:
|
||||||
// Four byte sequence:
|
|
||||||
elseif (($value & 0xF8) === 0xF0) {
|
|
||||||
$character = ($value & 0x07) << 18;
|
$character = ($value & 0x07) << 18;
|
||||||
$length = 4;
|
$length = 4;
|
||||||
$remaining = 3;
|
$remaining = 3;
|
||||||
}
|
} else { // Invalid byte:
|
||||||
// Invalid byte:
|
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value);
|
||||||
else {
|
|
||||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($remaining > 0) {
|
if ($remaining > 0) {
|
||||||
if ($position + $length > $strlen) {
|
if ($position + $length > $strlen) {
|
||||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($position++; $remaining > 0; $position++) {
|
for ($position++; $remaining > 0; $position++) {
|
||||||
$value = ord($input[$position]);
|
$value = ord($input[$position]);
|
||||||
|
|
||||||
// If it is invalid, count the sequence as invalid and reprocess the current byte:
|
// If it is invalid, count the sequence as invalid and reprocess the current byte:
|
||||||
if (($value & 0xC0) !== 0x80) {
|
if (($value & 0xC0) !== 0x80) {
|
||||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||||
}
|
}
|
||||||
|
|
||||||
--$remaining;
|
--$remaining;
|
||||||
$character |= ($value & 0x3F) << ($remaining * 6);
|
$character |= ($value & 0x3F) << ($remaining * 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
$position--;
|
$position--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +231,7 @@ class Requests_IDNAEncoder {
|
||||||
|| $character > 0xEFFFD
|
|| $character > 0xEFFFD
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||||
}
|
}
|
||||||
|
|
||||||
$codepoints[] = $character;
|
$codepoints[] = $character;
|
||||||
|
@ -221,10 +244,11 @@ class Requests_IDNAEncoder {
|
||||||
* RFC3492-compliant encoder
|
* RFC3492-compliant encoder
|
||||||
*
|
*
|
||||||
* @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
|
* @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
|
||||||
* @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
|
|
||||||
*
|
*
|
||||||
* @param string $input UTF-8 encoded string to encode
|
* @param string $input UTF-8 encoded string to encode
|
||||||
* @return string Punycode-encoded string
|
* @return string Punycode-encoded string
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
|
||||||
*/
|
*/
|
||||||
public static function punycode_encode($input) {
|
public static function punycode_encode($input) {
|
||||||
$output = '';
|
$output = '';
|
||||||
|
@ -239,7 +263,7 @@ class Requests_IDNAEncoder {
|
||||||
$b = 0; // see loop
|
$b = 0; // see loop
|
||||||
// copy them to the output in order
|
// copy them to the output in order
|
||||||
$codepoints = self::utf8_to_codepoints($input);
|
$codepoints = self::utf8_to_codepoints($input);
|
||||||
$extended = array();
|
$extended = [];
|
||||||
|
|
||||||
foreach ($codepoints as $char) {
|
foreach ($codepoints as $char) {
|
||||||
if ($char < 128) {
|
if ($char < 128) {
|
||||||
|
@ -247,18 +271,18 @@ class Requests_IDNAEncoder {
|
||||||
// TODO: this should also check if it's valid for a URL
|
// TODO: this should also check if it's valid for a URL
|
||||||
$output .= chr($char);
|
$output .= chr($char);
|
||||||
$h++;
|
$h++;
|
||||||
}
|
|
||||||
// Check if the character is non-ASCII, but below initial n
|
// Check if the character is non-ASCII, but below initial n
|
||||||
// This never occurs for Punycode, so ignore in coverage
|
// This never occurs for Punycode, so ignore in coverage
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
elseif ($char < $n) {
|
} elseif ($char < $n) {
|
||||||
throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char);
|
throw new Exception('Invalid character', 'idna.character_outside_domain', $char);
|
||||||
}
|
// @codeCoverageIgnoreEnd
|
||||||
// @codeCoverageIgnoreEnd
|
} else {
|
||||||
else {
|
|
||||||
$extended[$char] = true;
|
$extended[$char] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$extended = array_keys($extended);
|
$extended = array_keys($extended);
|
||||||
sort($extended);
|
sort($extended);
|
||||||
$b = $h;
|
$b = $h;
|
||||||
|
@ -266,6 +290,7 @@ class Requests_IDNAEncoder {
|
||||||
if (strlen($output) > 0) {
|
if (strlen($output) > 0) {
|
||||||
$output .= '-';
|
$output .= '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
// {if the input contains a non-basic code point < n then fail}
|
// {if the input contains a non-basic code point < n then fail}
|
||||||
// while h < length(input) do begin
|
// while h < length(input) do begin
|
||||||
$codepointcount = count($codepoints);
|
$codepointcount = count($codepoints);
|
||||||
|
@ -283,9 +308,7 @@ class Requests_IDNAEncoder {
|
||||||
// if c < n then increment delta, fail on overflow
|
// if c < n then increment delta, fail on overflow
|
||||||
if ($c < $n) {
|
if ($c < $n) {
|
||||||
$delta++;
|
$delta++;
|
||||||
}
|
} elseif ($c === $n) { // if c == n then begin
|
||||||
// if c == n then begin
|
|
||||||
elseif ($c === $n) {
|
|
||||||
// let q = delta
|
// let q = delta
|
||||||
$q = $delta;
|
$q = $delta;
|
||||||
// for k = base to infinity in steps of base do begin
|
// for k = base to infinity in steps of base do begin
|
||||||
|
@ -294,17 +317,17 @@ class Requests_IDNAEncoder {
|
||||||
// tmax if k >= bias + tmax, or k - bias otherwise
|
// tmax if k >= bias + tmax, or k - bias otherwise
|
||||||
if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
|
if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
|
||||||
$t = self::BOOTSTRAP_TMIN;
|
$t = self::BOOTSTRAP_TMIN;
|
||||||
}
|
} elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
|
||||||
elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
|
|
||||||
$t = self::BOOTSTRAP_TMAX;
|
$t = self::BOOTSTRAP_TMAX;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$t = $k - $bias;
|
$t = $k - $bias;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if q < t then break
|
// if q < t then break
|
||||||
if ($q < $t) {
|
if ($q < $t) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// output the code point for digit t + ((q - t) mod (base - t))
|
// output the code point for digit t + ((q - t) mod (base - t))
|
||||||
$digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t));
|
$digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t));
|
||||||
$output .= self::digit_to_char($digit);
|
$output .= self::digit_to_char($digit);
|
||||||
|
@ -332,18 +355,20 @@ class Requests_IDNAEncoder {
|
||||||
/**
|
/**
|
||||||
* Convert a digit to its respective character
|
* Convert a digit to its respective character
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc3492#section-5
|
* @link https://tools.ietf.org/html/rfc3492#section-5
|
||||||
* @throws Requests_Exception On invalid digit (`idna.invalid_digit`)
|
|
||||||
*
|
*
|
||||||
* @param int $digit Digit in the range 0-35
|
* @param int $digit Digit in the range 0-35
|
||||||
* @return string Single character corresponding to digit
|
* @return string Single character corresponding to digit
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On invalid digit (`idna.invalid_digit`)
|
||||||
*/
|
*/
|
||||||
protected static function digit_to_char($digit) {
|
protected static function digit_to_char($digit) {
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
// As far as I know, this never happens, but still good to be sure.
|
// As far as I know, this never happens, but still good to be sure.
|
||||||
if ($digit < 0 || $digit > 35) {
|
if ($digit < 0 || $digit > 35) {
|
||||||
throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
|
throw new Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
return substr($digits, $digit, 1);
|
return substr($digits, $digit, 1);
|
||||||
|
@ -352,7 +377,7 @@ class Requests_IDNAEncoder {
|
||||||
/**
|
/**
|
||||||
* Adapt the bias
|
* Adapt the bias
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc3492#section-6.1
|
* @link https://tools.ietf.org/html/rfc3492#section-6.1
|
||||||
* @param int $delta
|
* @param int $delta
|
||||||
* @param int $numpoints
|
* @param int $numpoints
|
||||||
* @param bool $firsttime
|
* @param bool $firsttime
|
||||||
|
@ -364,11 +389,11 @@ class Requests_IDNAEncoder {
|
||||||
// if firsttime then let delta = delta div damp
|
// if firsttime then let delta = delta div damp
|
||||||
if ($firsttime) {
|
if ($firsttime) {
|
||||||
$delta = floor($delta / self::BOOTSTRAP_DAMP);
|
$delta = floor($delta / self::BOOTSTRAP_DAMP);
|
||||||
}
|
} else {
|
||||||
// else let delta = delta div 2
|
// else let delta = delta div 2
|
||||||
else {
|
|
||||||
$delta = floor($delta / 2);
|
$delta = floor($delta / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// let delta = delta + (delta div numpoints)
|
// let delta = delta + (delta div numpoints)
|
||||||
$delta += floor($delta / $numpoints);
|
$delta += floor($delta / $numpoints);
|
||||||
// let k = 0
|
// let k = 0
|
|
@ -2,20 +2,23 @@
|
||||||
/**
|
/**
|
||||||
* Class to validate and to work with IPv6 addresses
|
* Class to validate and to work with IPv6 addresses
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to validate and to work with IPv6 addresses
|
* Class to validate and to work with IPv6 addresses
|
||||||
*
|
*
|
||||||
* This was originally based on the PEAR class of the same name, but has been
|
* This was originally based on the PEAR class of the same name, but has been
|
||||||
* entirely rewritten.
|
* entirely rewritten.
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
class Requests_IPv6 {
|
final class Ipv6 {
|
||||||
/**
|
/**
|
||||||
* Uncompresses an IPv6 address
|
* Uncompresses an IPv6 address
|
||||||
*
|
*
|
||||||
|
@ -30,11 +33,20 @@ class Requests_IPv6 {
|
||||||
* @author elfrink at introweb dot nl
|
* @author elfrink at introweb dot nl
|
||||||
* @author Josh Peck <jmp at joshpeck dot org>
|
* @author Josh Peck <jmp at joshpeck dot org>
|
||||||
* @copyright 2003-2005 The PHP Group
|
* @copyright 2003-2005 The PHP Group
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
* @license https://opensource.org/licenses/bsd-license.php
|
||||||
* @param string $ip An IPv6 address
|
*
|
||||||
|
* @param string|Stringable $ip An IPv6 address
|
||||||
* @return string The uncompressed IPv6 address
|
* @return string The uncompressed IPv6 address
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
|
||||||
*/
|
*/
|
||||||
public static function uncompress($ip) {
|
public static function uncompress($ip) {
|
||||||
|
if (InputValidator::is_string_or_stringable($ip) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$ip', 'string|Stringable', gettype($ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ip = (string) $ip;
|
||||||
|
|
||||||
if (substr_count($ip, '::') !== 1) {
|
if (substr_count($ip, '::') !== 1) {
|
||||||
return $ip;
|
return $ip;
|
||||||
}
|
}
|
||||||
|
@ -46,25 +58,24 @@ class Requests_IPv6 {
|
||||||
if (strpos($ip2, '.') !== false) {
|
if (strpos($ip2, '.') !== false) {
|
||||||
$c2++;
|
$c2++;
|
||||||
}
|
}
|
||||||
// ::
|
|
||||||
if ($c1 === -1 && $c2 === -1) {
|
if ($c1 === -1 && $c2 === -1) {
|
||||||
|
// ::
|
||||||
$ip = '0:0:0:0:0:0:0:0';
|
$ip = '0:0:0:0:0:0:0:0';
|
||||||
}
|
} elseif ($c1 === -1) {
|
||||||
// ::xxx
|
// ::xxx
|
||||||
elseif ($c1 === -1) {
|
|
||||||
$fill = str_repeat('0:', 7 - $c2);
|
$fill = str_repeat('0:', 7 - $c2);
|
||||||
$ip = str_replace('::', $fill, $ip);
|
$ip = str_replace('::', $fill, $ip);
|
||||||
}
|
} elseif ($c2 === -1) {
|
||||||
// xxx::
|
// xxx::
|
||||||
elseif ($c2 === -1) {
|
|
||||||
$fill = str_repeat(':0', 7 - $c1);
|
$fill = str_repeat(':0', 7 - $c1);
|
||||||
$ip = str_replace('::', $fill, $ip);
|
$ip = str_replace('::', $fill, $ip);
|
||||||
}
|
} else {
|
||||||
// xxx::xxx
|
// xxx::xxx
|
||||||
else {
|
|
||||||
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
|
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
|
||||||
$ip = str_replace('::', $fill, $ip);
|
$ip = str_replace('::', $fill, $ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ip;
|
return $ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,12 +89,14 @@ class Requests_IPv6 {
|
||||||
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
|
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
|
||||||
* 0:0:0:0:0:0:0:1 -> ::1
|
* 0:0:0:0:0:0:0:1 -> ::1
|
||||||
*
|
*
|
||||||
* @see uncompress()
|
* @see \WpOrg\Requests\Ipv6::uncompress()
|
||||||
|
*
|
||||||
* @param string $ip An IPv6 address
|
* @param string $ip An IPv6 address
|
||||||
* @return string The compressed IPv6 address
|
* @return string The compressed IPv6 address
|
||||||
*/
|
*/
|
||||||
public static function compress($ip) {
|
public static function compress($ip) {
|
||||||
// Prepare the IP to be compressed
|
// Prepare the IP to be compressed.
|
||||||
|
// Note: Input validation is handled in the `uncompress()` method, which is the first call made in this method.
|
||||||
$ip = self::uncompress($ip);
|
$ip = self::uncompress($ip);
|
||||||
$ip_parts = self::split_v6_v4($ip);
|
$ip_parts = self::split_v6_v4($ip);
|
||||||
|
|
||||||
|
@ -106,8 +119,7 @@ class Requests_IPv6 {
|
||||||
|
|
||||||
if ($ip_parts[1] !== '') {
|
if ($ip_parts[1] !== '') {
|
||||||
return implode(':', $ip_parts);
|
return implode(':', $ip_parts);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return $ip_parts[0];
|
return $ip_parts[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,15 +136,14 @@ class Requests_IPv6 {
|
||||||
* @param string $ip An IPv6 address
|
* @param string $ip An IPv6 address
|
||||||
* @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
|
* @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
|
||||||
*/
|
*/
|
||||||
protected static function split_v6_v4($ip) {
|
private static function split_v6_v4($ip) {
|
||||||
if (strpos($ip, '.') !== false) {
|
if (strpos($ip, '.') !== false) {
|
||||||
$pos = strrpos($ip, ':');
|
$pos = strrpos($ip, ':');
|
||||||
$ipv6_part = substr($ip, 0, $pos);
|
$ipv6_part = substr($ip, 0, $pos);
|
||||||
$ipv4_part = substr($ip, $pos + 1);
|
$ipv4_part = substr($ip, $pos + 1);
|
||||||
return array($ipv6_part, $ipv4_part);
|
return [$ipv6_part, $ipv4_part];
|
||||||
}
|
} else {
|
||||||
else {
|
return [$ip, ''];
|
||||||
return array($ip, '');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +156,7 @@ class Requests_IPv6 {
|
||||||
* @return bool true if $ip is a valid IPv6 address
|
* @return bool true if $ip is a valid IPv6 address
|
||||||
*/
|
*/
|
||||||
public static function check_ipv6($ip) {
|
public static function check_ipv6($ip) {
|
||||||
|
// Note: Input validation is handled in the `uncompress()` method, which is the first call made in this method.
|
||||||
$ip = self::uncompress($ip);
|
$ip = self::uncompress($ip);
|
||||||
list($ipv6, $ipv4) = self::split_v6_v4($ip);
|
list($ipv6, $ipv4) = self::split_v6_v4($ip);
|
||||||
$ipv6 = explode(':', $ipv6);
|
$ipv6 = explode(':', $ipv6);
|
||||||
|
@ -173,6 +185,7 @@ class Requests_IPv6 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($ipv4) === 4) {
|
if (count($ipv4) === 4) {
|
||||||
foreach ($ipv4 as $ipv4_part) {
|
foreach ($ipv4 as $ipv4_part) {
|
||||||
$value = (int) $ipv4_part;
|
$value = (int) $ipv4_part;
|
||||||
|
@ -181,9 +194,9 @@ class Requests_IPv6 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,10 +2,17 @@
|
||||||
/**
|
/**
|
||||||
* IRI parser/serialiser/normaliser
|
* IRI parser/serialiser/normaliser
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Ipv6;
|
||||||
|
use WpOrg\Requests\Port;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IRI parser/serialiser/normaliser
|
* IRI parser/serialiser/normaliser
|
||||||
*
|
*
|
||||||
|
@ -38,16 +45,15 @@
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
|
||||||
* @author Geoffrey Sneddon
|
* @author Geoffrey Sneddon
|
||||||
* @author Steve Minutillo
|
* @author Steve Minutillo
|
||||||
* @copyright 2007-2009 Geoffrey Sneddon and Steve Minutillo
|
* @copyright 2007-2009 Geoffrey Sneddon and Steve Minutillo
|
||||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
* @license https://opensource.org/licenses/bsd-license.php
|
||||||
* @link http://hg.gsnedders.com/iri/
|
* @link http://hg.gsnedders.com/iri/
|
||||||
*
|
*
|
||||||
* @property string $iri IRI we're working with
|
* @property string $iri IRI we're working with
|
||||||
* @property-read string $uri IRI in URI form, {@see to_uri}
|
* @property-read string $uri IRI in URI form, {@see \WpOrg\Requests\Iri::to_uri()}
|
||||||
* @property string $scheme Scheme part of the IRI
|
* @property string $scheme Scheme part of the IRI
|
||||||
* @property string $authority Authority part, formatted for a URI (userinfo + host + port)
|
* @property string $authority Authority part, formatted for a URI (userinfo + host + port)
|
||||||
* @property string $iauthority Authority part of the IRI (userinfo + host + port)
|
* @property string $iauthority Authority part of the IRI (userinfo + host + port)
|
||||||
|
@ -63,7 +69,7 @@
|
||||||
* @property string $fragment Fragment, formatted for a URI (after '#')
|
* @property string $fragment Fragment, formatted for a URI (after '#')
|
||||||
* @property string $ifragment Fragment part of the IRI (after '#')
|
* @property string $ifragment Fragment part of the IRI (after '#')
|
||||||
*/
|
*/
|
||||||
class Requests_IRI {
|
class Iri {
|
||||||
/**
|
/**
|
||||||
* Scheme
|
* Scheme
|
||||||
*
|
*
|
||||||
|
@ -123,19 +129,19 @@ class Requests_IRI {
|
||||||
*/
|
*/
|
||||||
protected $normalization = array(
|
protected $normalization = array(
|
||||||
'acap' => array(
|
'acap' => array(
|
||||||
'port' => 674
|
'port' => Port::ACAP,
|
||||||
),
|
),
|
||||||
'dict' => array(
|
'dict' => array(
|
||||||
'port' => 2628
|
'port' => Port::DICT,
|
||||||
),
|
),
|
||||||
'file' => array(
|
'file' => array(
|
||||||
'ihost' => 'localhost'
|
'ihost' => 'localhost',
|
||||||
),
|
),
|
||||||
'http' => array(
|
'http' => array(
|
||||||
'port' => 80,
|
'port' => Port::HTTP,
|
||||||
),
|
),
|
||||||
'https' => array(
|
'https' => array(
|
||||||
'port' => 443,
|
'port' => Port::HTTPS,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -240,9 +246,15 @@ class Requests_IRI {
|
||||||
/**
|
/**
|
||||||
* Create a new IRI object, from a specified string
|
* Create a new IRI object, from a specified string
|
||||||
*
|
*
|
||||||
* @param string|null $iri
|
* @param string|Stringable|null $iri
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $iri argument is not a string, Stringable or null.
|
||||||
*/
|
*/
|
||||||
public function __construct($iri = null) {
|
public function __construct($iri = null) {
|
||||||
|
if ($iri !== null && InputValidator::is_string_or_stringable($iri) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$iri', 'string|Stringable|null', gettype($iri));
|
||||||
|
}
|
||||||
|
|
||||||
$this->set_iri($iri);
|
$this->set_iri($iri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,13 +263,13 @@ class Requests_IRI {
|
||||||
*
|
*
|
||||||
* Returns false if $base is not absolute, otherwise an IRI.
|
* Returns false if $base is not absolute, otherwise an IRI.
|
||||||
*
|
*
|
||||||
* @param Requests_IRI|string $base (Absolute) Base IRI
|
* @param \WpOrg\Requests\Iri|string $base (Absolute) Base IRI
|
||||||
* @param Requests_IRI|string $relative Relative IRI
|
* @param \WpOrg\Requests\Iri|string $relative Relative IRI
|
||||||
* @return Requests_IRI|false
|
* @return \WpOrg\Requests\Iri|false
|
||||||
*/
|
*/
|
||||||
public static function absolutize($base, $relative) {
|
public static function absolutize($base, $relative) {
|
||||||
if (!($relative instanceof Requests_IRI)) {
|
if (!($relative instanceof self)) {
|
||||||
$relative = new Requests_IRI($relative);
|
$relative = new self($relative);
|
||||||
}
|
}
|
||||||
if (!$relative->is_valid()) {
|
if (!$relative->is_valid()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -266,8 +278,8 @@ class Requests_IRI {
|
||||||
return clone $relative;
|
return clone $relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!($base instanceof Requests_IRI)) {
|
if (!($base instanceof self)) {
|
||||||
$base = new Requests_IRI($base);
|
$base = new self($base);
|
||||||
}
|
}
|
||||||
if ($base->scheme === null || !$base->is_valid()) {
|
if ($base->scheme === null || !$base->is_valid()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -279,7 +291,7 @@ class Requests_IRI {
|
||||||
$target->scheme = $base->scheme;
|
$target->scheme = $base->scheme;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$target = new Requests_IRI;
|
$target = new self;
|
||||||
$target->scheme = $base->scheme;
|
$target->scheme = $base->scheme;
|
||||||
$target->iuserinfo = $base->iuserinfo;
|
$target->iuserinfo = $base->iuserinfo;
|
||||||
$target->ihost = $base->ihost;
|
$target->ihost = $base->ihost;
|
||||||
|
@ -330,7 +342,7 @@ class Requests_IRI {
|
||||||
$iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
|
$iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
|
||||||
$has_match = preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match);
|
$has_match = preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match);
|
||||||
if (!$has_match) {
|
if (!$has_match) {
|
||||||
throw new Requests_Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri);
|
throw new Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($match[1] === '') {
|
if ($match[1] === '') {
|
||||||
|
@ -413,18 +425,18 @@ class Requests_IRI {
|
||||||
/**
|
/**
|
||||||
* Replace invalid character with percent encoding
|
* Replace invalid character with percent encoding
|
||||||
*
|
*
|
||||||
* @param string $string Input string
|
* @param string $text Input string
|
||||||
* @param string $extra_chars Valid characters not in iunreserved or
|
* @param string $extra_chars Valid characters not in iunreserved or
|
||||||
* iprivate (this is ASCII-only)
|
* iprivate (this is ASCII-only)
|
||||||
* @param bool $iprivate Allow iprivate
|
* @param bool $iprivate Allow iprivate
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) {
|
protected function replace_invalid_with_pct_encoding($text, $extra_chars, $iprivate = false) {
|
||||||
// Normalize as many pct-encoded sections as possible
|
// Normalize as many pct-encoded sections as possible
|
||||||
$string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
|
$text = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $text);
|
||||||
|
|
||||||
// Replace invalid percent characters
|
// Replace invalid percent characters
|
||||||
$string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
|
$text = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $text);
|
||||||
|
|
||||||
// Add unreserved and % to $extra_chars (the latter is safe because all
|
// Add unreserved and % to $extra_chars (the latter is safe because all
|
||||||
// pct-encoded sections are now valid).
|
// pct-encoded sections are now valid).
|
||||||
|
@ -432,9 +444,9 @@ class Requests_IRI {
|
||||||
|
|
||||||
// Now replace any bytes that aren't allowed with their pct-encoded versions
|
// Now replace any bytes that aren't allowed with their pct-encoded versions
|
||||||
$position = 0;
|
$position = 0;
|
||||||
$strlen = strlen($string);
|
$strlen = strlen($text);
|
||||||
while (($position += strspn($string, $extra_chars, $position)) < $strlen) {
|
while (($position += strspn($text, $extra_chars, $position)) < $strlen) {
|
||||||
$value = ord($string[$position]);
|
$value = ord($text[$position]);
|
||||||
|
|
||||||
// Start position
|
// Start position
|
||||||
$start = $position;
|
$start = $position;
|
||||||
|
@ -471,7 +483,7 @@ class Requests_IRI {
|
||||||
if ($remaining) {
|
if ($remaining) {
|
||||||
if ($position + $length <= $strlen) {
|
if ($position + $length <= $strlen) {
|
||||||
for ($position++; $remaining; $position++) {
|
for ($position++; $remaining; $position++) {
|
||||||
$value = ord($string[$position]);
|
$value = ord($text[$position]);
|
||||||
|
|
||||||
// Check that the byte is valid, then add it to the character:
|
// Check that the byte is valid, then add it to the character:
|
||||||
if (($value & 0xC0) === 0x80) {
|
if (($value & 0xC0) === 0x80) {
|
||||||
|
@ -522,7 +534,7 @@ class Requests_IRI {
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($j = $start; $j <= $position; $j++) {
|
for ($j = $start; $j <= $position; $j++) {
|
||||||
$string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
|
$text = substr_replace($text, sprintf('%%%02X', ord($text[$j])), $j, 1);
|
||||||
$j += 2;
|
$j += 2;
|
||||||
$position += 2;
|
$position += 2;
|
||||||
$strlen += 2;
|
$strlen += 2;
|
||||||
|
@ -530,7 +542,7 @@ class Requests_IRI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $string;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -539,13 +551,13 @@ class Requests_IRI {
|
||||||
* Removes sequences of percent encoded bytes that represent UTF-8
|
* Removes sequences of percent encoded bytes that represent UTF-8
|
||||||
* encoded characters in iunreserved
|
* encoded characters in iunreserved
|
||||||
*
|
*
|
||||||
* @param array $match PCRE match
|
* @param array $regex_match PCRE match
|
||||||
* @return string Replacement
|
* @return string Replacement
|
||||||
*/
|
*/
|
||||||
protected function remove_iunreserved_percent_encoded($match) {
|
protected function remove_iunreserved_percent_encoded($regex_match) {
|
||||||
// As we just have valid percent encoded sequences we can just explode
|
// As we just have valid percent encoded sequences we can just explode
|
||||||
// and ignore the first member of the returned array (an empty string).
|
// and ignore the first member of the returned array (an empty string).
|
||||||
$bytes = explode('%', $match[0]);
|
$bytes = explode('%', $regex_match[0]);
|
||||||
|
|
||||||
// Initialize the new string (this is what will be returned) and that
|
// Initialize the new string (this is what will be returned) and that
|
||||||
// there are no bytes remaining in the current sequence (unsurprising
|
// there are no bytes remaining in the current sequence (unsurprising
|
||||||
|
@ -721,6 +733,9 @@ class Requests_IRI {
|
||||||
if ($iri === null) {
|
if ($iri === null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$iri = (string) $iri;
|
||||||
|
|
||||||
if (isset($cache[$iri])) {
|
if (isset($cache[$iri])) {
|
||||||
list($this->scheme,
|
list($this->scheme,
|
||||||
$this->iuserinfo,
|
$this->iuserinfo,
|
||||||
|
@ -733,7 +748,7 @@ class Requests_IRI {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsed = $this->parse_iri((string) $iri);
|
$parsed = $this->parse_iri($iri);
|
||||||
|
|
||||||
$return = $this->set_scheme($parsed['scheme'])
|
$return = $this->set_scheme($parsed['scheme'])
|
||||||
&& $this->set_authority($parsed['authority'])
|
&& $this->set_authority($parsed['authority'])
|
||||||
|
@ -863,8 +878,8 @@ class Requests_IRI {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') {
|
if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') {
|
||||||
if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) {
|
if (Ipv6::check_ipv6(substr($ihost, 1, -1))) {
|
||||||
$this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']';
|
$this->ihost = '[' . Ipv6::compress(substr($ihost, 1, -1)) . ']';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->ihost = null;
|
$this->ihost = null;
|
||||||
|
@ -985,11 +1000,11 @@ class Requests_IRI {
|
||||||
/**
|
/**
|
||||||
* Convert an IRI to a URI (or parts thereof)
|
* Convert an IRI to a URI (or parts thereof)
|
||||||
*
|
*
|
||||||
* @param string|bool IRI to convert (or false from {@see get_iri})
|
* @param string|bool $iri IRI to convert (or false from {@see \WpOrg\Requests\Iri::get_iri()})
|
||||||
* @return string|false URI if IRI is valid, false otherwise.
|
* @return string|false URI if IRI is valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
protected function to_uri($string) {
|
protected function to_uri($iri) {
|
||||||
if (!is_string($string)) {
|
if (!is_string($iri)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -999,14 +1014,14 @@ class Requests_IRI {
|
||||||
}
|
}
|
||||||
|
|
||||||
$position = 0;
|
$position = 0;
|
||||||
$strlen = strlen($string);
|
$strlen = strlen($iri);
|
||||||
while (($position += strcspn($string, $non_ascii, $position)) < $strlen) {
|
while (($position += strcspn($iri, $non_ascii, $position)) < $strlen) {
|
||||||
$string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
|
$iri = substr_replace($iri, sprintf('%%%02X', ord($iri[$position])), $position, 1);
|
||||||
$position += 3;
|
$position += 3;
|
||||||
$strlen += 2;
|
$strlen += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $string;
|
return $iri;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Port utilities for Requests
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the correct port depending on the Request type.
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
final class Port {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port to use with Acap requests.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const ACAP = 674;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port to use with Dictionary requests.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const DICT = 2628;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port to use with HTTP requests.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const HTTP = 80;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port to use with HTTP over SSL requests.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const HTTPS = 443;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the port number to use.
|
||||||
|
*
|
||||||
|
* @param string $type Request type.
|
||||||
|
* The following requests types are supported:
|
||||||
|
* 'acap', 'dict', 'http' and 'https'.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When a non-string input has been passed.
|
||||||
|
* @throws \WpOrg\Requests\Exception When a non-supported port is requested ('portnotsupported').
|
||||||
|
*/
|
||||||
|
public static function get($type) {
|
||||||
|
if (!is_string($type)) {
|
||||||
|
throw InvalidArgument::create(1, '$type', 'string', gettype($type));
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = strtoupper($type);
|
||||||
|
if (!defined("self::{$type}")) {
|
||||||
|
$message = sprintf('Invalid port type (%s) passed', $type);
|
||||||
|
throw new Exception($message, 'portnotsupported');
|
||||||
|
}
|
||||||
|
|
||||||
|
return constant("self::{$type}");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Proxy connection interface
|
||||||
|
*
|
||||||
|
* @package Requests\Proxy
|
||||||
|
* @since 1.6
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Hooks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy connection interface
|
||||||
|
*
|
||||||
|
* Implement this interface to handle proxy settings and authentication
|
||||||
|
*
|
||||||
|
* Parameters should be passed via the constructor where possible, as this
|
||||||
|
* makes it much easier for users to use your provider.
|
||||||
|
*
|
||||||
|
* @see \WpOrg\Requests\Hooks
|
||||||
|
*
|
||||||
|
* @package Requests\Proxy
|
||||||
|
* @since 1.6
|
||||||
|
*/
|
||||||
|
interface Proxy {
|
||||||
|
/**
|
||||||
|
* Register hooks as needed
|
||||||
|
*
|
||||||
|
* This method is called in {@see \WpOrg\Requests\Requests::request()} when the user
|
||||||
|
* has set an instance as the 'auth' option. Use this callback to register all the
|
||||||
|
* hooks you'll need.
|
||||||
|
*
|
||||||
|
* @see \WpOrg\Requests\Hooks::register()
|
||||||
|
* @param \WpOrg\Requests\Hooks $hooks Hook system
|
||||||
|
*/
|
||||||
|
public function register(Hooks $hooks);
|
||||||
|
}
|
|
@ -2,21 +2,26 @@
|
||||||
/**
|
/**
|
||||||
* HTTP Proxy connection interface
|
* HTTP Proxy connection interface
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Proxy
|
||||||
* @subpackage Proxy
|
* @since 1.6
|
||||||
* @since 1.6
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Proxy;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\ArgumentCount;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Hooks;
|
||||||
|
use WpOrg\Requests\Proxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP Proxy connection interface
|
* HTTP Proxy connection interface
|
||||||
*
|
*
|
||||||
* Provides a handler for connection via an HTTP proxy
|
* Provides a handler for connection via an HTTP proxy
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Proxy
|
||||||
* @subpackage Proxy
|
* @since 1.6
|
||||||
* @since 1.6
|
|
||||||
*/
|
*/
|
||||||
class Requests_Proxy_HTTP implements Requests_Proxy {
|
final class Http implements Proxy {
|
||||||
/**
|
/**
|
||||||
* Proxy host and port
|
* Proxy host and port
|
||||||
*
|
*
|
||||||
|
@ -51,24 +56,32 @@ class Requests_Proxy_HTTP implements Requests_Proxy {
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
|
*
|
||||||
* @param array|null $args Array of user and password. Must have exactly two elements
|
* @param array|string|null $args Proxy as a string or an array of proxy, user and password.
|
||||||
|
* When passed as an array, must have exactly one (proxy)
|
||||||
|
* or three elements (proxy, user, password).
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array, a string or null.
|
||||||
|
* @throws \WpOrg\Requests\Exception\ArgumentCount On incorrect number of arguments (`proxyhttpbadargs`)
|
||||||
*/
|
*/
|
||||||
public function __construct($args = null) {
|
public function __construct($args = null) {
|
||||||
if (is_string($args)) {
|
if (is_string($args)) {
|
||||||
$this->proxy = $args;
|
$this->proxy = $args;
|
||||||
}
|
} elseif (is_array($args)) {
|
||||||
elseif (is_array($args)) {
|
|
||||||
if (count($args) === 1) {
|
if (count($args) === 1) {
|
||||||
list($this->proxy) = $args;
|
list($this->proxy) = $args;
|
||||||
}
|
} elseif (count($args) === 3) {
|
||||||
elseif (count($args) === 3) {
|
|
||||||
list($this->proxy, $this->user, $this->pass) = $args;
|
list($this->proxy, $this->user, $this->pass) = $args;
|
||||||
$this->use_authentication = true;
|
$this->use_authentication = true;
|
||||||
|
} else {
|
||||||
|
throw ArgumentCount::create(
|
||||||
|
'an array with exactly one element or exactly three elements',
|
||||||
|
count($args),
|
||||||
|
'proxyhttpbadargs'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
} elseif ($args !== null) {
|
||||||
throw new Requests_Exception('Invalid number of arguments', 'proxyhttpbadargs');
|
throw InvalidArgument::create(1, '$args', 'array|string|null', gettype($args));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,19 +89,19 @@ class Requests_Proxy_HTTP implements Requests_Proxy {
|
||||||
* Register the necessary callbacks
|
* Register the necessary callbacks
|
||||||
*
|
*
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
* @see curl_before_send
|
* @see \WpOrg\Requests\Proxy\Http::curl_before_send()
|
||||||
* @see fsockopen_remote_socket
|
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_socket()
|
||||||
* @see fsockopen_remote_host_path
|
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_host_path()
|
||||||
* @see fsockopen_header
|
* @see \WpOrg\Requests\Proxy\Http::fsockopen_header()
|
||||||
* @param Requests_Hooks $hooks Hook system
|
* @param \WpOrg\Requests\Hooks $hooks Hook system
|
||||||
*/
|
*/
|
||||||
public function register(Requests_Hooks $hooks) {
|
public function register(Hooks $hooks) {
|
||||||
$hooks->register('curl.before_send', array($this, 'curl_before_send'));
|
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
|
||||||
|
|
||||||
$hooks->register('fsockopen.remote_socket', array($this, 'fsockopen_remote_socket'));
|
$hooks->register('fsockopen.remote_socket', [$this, 'fsockopen_remote_socket']);
|
||||||
$hooks->register('fsockopen.remote_host_path', array($this, 'fsockopen_remote_host_path'));
|
$hooks->register('fsockopen.remote_host_path', [$this, 'fsockopen_remote_host_path']);
|
||||||
if ($this->use_authentication) {
|
if ($this->use_authentication) {
|
||||||
$hooks->register('fsockopen.after_headers', array($this, 'fsockopen_header'));
|
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +109,7 @@ class Requests_Proxy_HTTP implements Requests_Proxy {
|
||||||
* Set cURL parameters before the data is sent
|
* Set cURL parameters before the data is sent
|
||||||
*
|
*
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
* @param resource $handle cURL resource
|
* @param resource|\CurlHandle $handle cURL handle
|
||||||
*/
|
*/
|
||||||
public function curl_before_send(&$handle) {
|
public function curl_before_send(&$handle) {
|
||||||
curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,165 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HTTP response class
|
||||||
|
*
|
||||||
|
* Contains a response from \WpOrg\Requests\Requests::request()
|
||||||
|
*
|
||||||
|
* @package Requests
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Cookie\Jar;
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\Http;
|
||||||
|
use WpOrg\Requests\Response\Headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP response class
|
||||||
|
*
|
||||||
|
* Contains a response from \WpOrg\Requests\Requests::request()
|
||||||
|
*
|
||||||
|
* @package Requests
|
||||||
|
*/
|
||||||
|
class Response {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response body
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $body = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw HTTP data from the transport
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $raw = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Headers, as an associative array
|
||||||
|
*
|
||||||
|
* @var \WpOrg\Requests\Response\Headers Array-like object representing headers
|
||||||
|
*/
|
||||||
|
public $headers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status code, false if non-blocking
|
||||||
|
*
|
||||||
|
* @var integer|boolean
|
||||||
|
*/
|
||||||
|
public $status_code = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol version, false if non-blocking
|
||||||
|
*
|
||||||
|
* @var float|boolean
|
||||||
|
*/
|
||||||
|
public $protocol_version = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the request succeeded or not
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
public $success = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of redirects the request used
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
public $redirects = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL requested
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $url = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous requests (from redirects)
|
||||||
|
*
|
||||||
|
* @var array Array of \WpOrg\Requests\Response objects
|
||||||
|
*/
|
||||||
|
public $history = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cookies from the request
|
||||||
|
*
|
||||||
|
* @var \WpOrg\Requests\Cookie\Jar Array-like object representing a cookie jar
|
||||||
|
*/
|
||||||
|
public $cookies = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
$this->headers = new Headers();
|
||||||
|
$this->cookies = new Jar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the response a redirect?
|
||||||
|
*
|
||||||
|
* @return boolean True if redirect (3xx status), false if not.
|
||||||
|
*/
|
||||||
|
public function is_redirect() {
|
||||||
|
$code = $this->status_code;
|
||||||
|
return in_array($code, [300, 301, 302, 303, 307], true) || $code > 307 && $code < 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an exception if the request was not successful
|
||||||
|
*
|
||||||
|
* @param boolean $allow_redirects Set to false to throw on a 3xx as well
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`)
|
||||||
|
* @throws \WpOrg\Requests\Exception\Http On non-successful status code. Exception class corresponds to "Status" + code (e.g. {@see \WpOrg\Requests\Exception\Http\Status404})
|
||||||
|
*/
|
||||||
|
public function throw_for_status($allow_redirects = true) {
|
||||||
|
if ($this->is_redirect()) {
|
||||||
|
if ($allow_redirects !== true) {
|
||||||
|
throw new Exception('Redirection not allowed', 'response.no_redirects', $this);
|
||||||
|
}
|
||||||
|
} elseif (!$this->success) {
|
||||||
|
$exception = Http::get_class($this->status_code);
|
||||||
|
throw new $exception(null, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON decode the response body.
|
||||||
|
*
|
||||||
|
* The method parameters are the same as those for the PHP native `json_decode()` function.
|
||||||
|
*
|
||||||
|
* @link https://php.net/json-decode
|
||||||
|
*
|
||||||
|
* @param ?bool $associative Optional. When `true`, JSON objects will be returned as associative arrays;
|
||||||
|
* When `false`, JSON objects will be returned as objects.
|
||||||
|
* When `null`, JSON objects will be returned as associative arrays
|
||||||
|
* or objects depending on whether `JSON_OBJECT_AS_ARRAY` is set in the flags.
|
||||||
|
* Defaults to `true` (in contrast to the PHP native default of `null`).
|
||||||
|
* @param int $depth Optional. Maximum nesting depth of the structure being decoded.
|
||||||
|
* Defaults to `512`.
|
||||||
|
* @param int $options Optional. Bitmask of JSON_BIGINT_AS_STRING, JSON_INVALID_UTF8_IGNORE,
|
||||||
|
* JSON_INVALID_UTF8_SUBSTITUTE, JSON_OBJECT_AS_ARRAY, JSON_THROW_ON_ERROR.
|
||||||
|
* Defaults to `0` (no options set).
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception If `$this->body` is not valid json.
|
||||||
|
*/
|
||||||
|
public function decode_body($associative = true, $depth = 512, $options = 0) {
|
||||||
|
$data = json_decode($this->body, $associative, $depth, $options);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
$last_error = json_last_error_msg();
|
||||||
|
throw new Exception('Unable to parse JSON data: ' . $last_error, 'response.invalid', $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Case-insensitive dictionary, suitable for HTTP headers
|
||||||
|
*
|
||||||
|
* @package Requests
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Response;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
|
||||||
|
use WpOrg\Requests\Utility\FilteredIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case-insensitive dictionary, suitable for HTTP headers
|
||||||
|
*
|
||||||
|
* @package Requests
|
||||||
|
*/
|
||||||
|
class Headers extends CaseInsensitiveDictionary {
|
||||||
|
/**
|
||||||
|
* Get the given header
|
||||||
|
*
|
||||||
|
* Unlike {@see \WpOrg\Requests\Response\Headers::getValues()}, this returns a string. If there are
|
||||||
|
* multiple values, it concatenates them with a comma as per RFC2616.
|
||||||
|
*
|
||||||
|
* Avoid using this where commas may be used unquoted in values, such as
|
||||||
|
* Set-Cookie headers.
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
* @return string|null Header value
|
||||||
|
*/
|
||||||
|
public function offsetGet($offset) {
|
||||||
|
if (is_string($offset)) {
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->data[$offset])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->flatten($this->data[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the given item
|
||||||
|
*
|
||||||
|
* @param string $offset Item name
|
||||||
|
* @param string $value Item value
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value) {
|
||||||
|
if ($offset === null) {
|
||||||
|
throw new Exception('Object is a dictionary, not a list', 'invalidset');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($offset)) {
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->data[$offset])) {
|
||||||
|
$this->data[$offset] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->data[$offset][] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all values for a given header
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
* @return array|null Header values
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not valid as an array key.
|
||||||
|
*/
|
||||||
|
public function getValues($offset) {
|
||||||
|
if (!is_string($offset) && !is_int($offset)) {
|
||||||
|
throw InvalidArgument::create(1, '$offset', 'string|int', gettype($offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
if (!isset($this->data[$offset])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->data[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flattens a value into a string
|
||||||
|
*
|
||||||
|
* Converts an array into a string by imploding values with a comma, as per
|
||||||
|
* RFC2616's rules for folding headers.
|
||||||
|
*
|
||||||
|
* @param string|array $value Value to flatten
|
||||||
|
* @return string Flattened value
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or an array.
|
||||||
|
*/
|
||||||
|
public function flatten($value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
return implode(',', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw InvalidArgument::create(1, '$value', 'string|array', gettype($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator for the data
|
||||||
|
*
|
||||||
|
* Converts the internally stored values to a comma-separated string if there is more
|
||||||
|
* than one value for a key.
|
||||||
|
*
|
||||||
|
* @return \ArrayIterator
|
||||||
|
*/
|
||||||
|
public function getIterator() {
|
||||||
|
return new FilteredIterator($this->data, [$this, 'flatten']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,17 @@
|
||||||
/**
|
/**
|
||||||
* Session handler for persistent requests and default parameters
|
* Session handler for persistent requests and default parameters
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\SessionHandler
|
||||||
* @subpackage Session Handler
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Cookie\Jar;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Iri;
|
||||||
|
use WpOrg\Requests\Requests;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session handler for persistent requests and default parameters
|
* Session handler for persistent requests and default parameters
|
||||||
*
|
*
|
||||||
|
@ -14,10 +21,9 @@
|
||||||
* with all subrequests resolved from this. Base options can be set (including
|
* with all subrequests resolved from this. Base options can be set (including
|
||||||
* a shared cookie jar), then overridden for individual requests.
|
* a shared cookie jar), then overridden for individual requests.
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\SessionHandler
|
||||||
* @subpackage Session Handler
|
|
||||||
*/
|
*/
|
||||||
class Requests_Session {
|
class Session {
|
||||||
/**
|
/**
|
||||||
* Base URL for requests
|
* Base URL for requests
|
||||||
*
|
*
|
||||||
|
@ -32,7 +38,7 @@ class Requests_Session {
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $headers = array();
|
public $headers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base data for requests
|
* Base data for requests
|
||||||
|
@ -42,7 +48,7 @@ class Requests_Session {
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $data = array();
|
public $data = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base options for requests
|
* Base options for requests
|
||||||
|
@ -55,36 +61,57 @@ class Requests_Session {
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $options = array();
|
public $options = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session
|
* Create a new session
|
||||||
*
|
*
|
||||||
* @param string|null $url Base URL for requests
|
* @param string|Stringable|null $url Base URL for requests
|
||||||
* @param array $headers Default headers for requests
|
* @param array $headers Default headers for requests
|
||||||
* @param array $data Default data for requests
|
* @param array $data Default data for requests
|
||||||
* @param array $options Default options for requests
|
* @param array $options Default options for requests
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string, Stringable or null.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||||
*/
|
*/
|
||||||
public function __construct($url = null, $headers = array(), $data = array(), $options = array()) {
|
public function __construct($url = null, $headers = [], $data = [], $options = []) {
|
||||||
|
if ($url !== null && InputValidator::is_string_or_stringable($url) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$url', 'string|Stringable|null', gettype($url));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($headers) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($data) === false) {
|
||||||
|
throw InvalidArgument::create(3, '$data', 'array', gettype($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($options) === false) {
|
||||||
|
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
|
||||||
|
}
|
||||||
|
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
$this->headers = $headers;
|
$this->headers = $headers;
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
|
|
||||||
if (empty($this->options['cookies'])) {
|
if (empty($this->options['cookies'])) {
|
||||||
$this->options['cookies'] = new Requests_Cookie_Jar();
|
$this->options['cookies'] = new Jar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a property's value
|
* Get a property's value
|
||||||
*
|
*
|
||||||
* @param string $key Property key
|
* @param string $name Property name.
|
||||||
* @return mixed|null Property value, null if none found
|
* @return mixed|null Property value, null if none found
|
||||||
*/
|
*/
|
||||||
public function __get($key) {
|
public function __get($name) {
|
||||||
if (isset($this->options[$key])) {
|
if (isset($this->options[$name])) {
|
||||||
return $this->options[$key];
|
return $this->options[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -93,93 +120,91 @@ class Requests_Session {
|
||||||
/**
|
/**
|
||||||
* Set a property's value
|
* Set a property's value
|
||||||
*
|
*
|
||||||
* @param string $key Property key
|
* @param string $name Property name.
|
||||||
* @param mixed $value Property value
|
* @param mixed $value Property value
|
||||||
*/
|
*/
|
||||||
public function __set($key, $value) {
|
public function __set($name, $value) {
|
||||||
$this->options[$key] = $value;
|
$this->options[$name] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a property's value
|
* Remove a property's value
|
||||||
*
|
*
|
||||||
* @param string $key Property key
|
* @param string $name Property name.
|
||||||
*/
|
*/
|
||||||
public function __isset($key) {
|
public function __isset($name) {
|
||||||
return isset($this->options[$key]);
|
return isset($this->options[$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a property's value
|
* Remove a property's value
|
||||||
*
|
*
|
||||||
* @param string $key Property key
|
* @param string $name Property name.
|
||||||
*/
|
*/
|
||||||
public function __unset($key) {
|
public function __unset($name) {
|
||||||
if (isset($this->options[$key])) {
|
unset($this->options[$name]);
|
||||||
unset($this->options[$key]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**#@+
|
/**#@+
|
||||||
* @see request()
|
* @see \WpOrg\Requests\Session::request()
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $headers
|
* @param array $headers
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Requests_Response
|
* @return \WpOrg\Requests\Response
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Send a GET request
|
* Send a GET request
|
||||||
*/
|
*/
|
||||||
public function get($url, $headers = array(), $options = array()) {
|
public function get($url, $headers = [], $options = []) {
|
||||||
return $this->request($url, $headers, null, Requests::GET, $options);
|
return $this->request($url, $headers, null, Requests::GET, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a HEAD request
|
* Send a HEAD request
|
||||||
*/
|
*/
|
||||||
public function head($url, $headers = array(), $options = array()) {
|
public function head($url, $headers = [], $options = []) {
|
||||||
return $this->request($url, $headers, null, Requests::HEAD, $options);
|
return $this->request($url, $headers, null, Requests::HEAD, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a DELETE request
|
* Send a DELETE request
|
||||||
*/
|
*/
|
||||||
public function delete($url, $headers = array(), $options = array()) {
|
public function delete($url, $headers = [], $options = []) {
|
||||||
return $this->request($url, $headers, null, Requests::DELETE, $options);
|
return $this->request($url, $headers, null, Requests::DELETE, $options);
|
||||||
}
|
}
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
/**#@+
|
/**#@+
|
||||||
* @see request()
|
* @see \WpOrg\Requests\Session::request()
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array $headers
|
* @param array $headers
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Requests_Response
|
* @return \WpOrg\Requests\Response
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Send a POST request
|
* Send a POST request
|
||||||
*/
|
*/
|
||||||
public function post($url, $headers = array(), $data = array(), $options = array()) {
|
public function post($url, $headers = [], $data = [], $options = []) {
|
||||||
return $this->request($url, $headers, $data, Requests::POST, $options);
|
return $this->request($url, $headers, $data, Requests::POST, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a PUT request
|
* Send a PUT request
|
||||||
*/
|
*/
|
||||||
public function put($url, $headers = array(), $data = array(), $options = array()) {
|
public function put($url, $headers = [], $data = [], $options = []) {
|
||||||
return $this->request($url, $headers, $data, Requests::PUT, $options);
|
return $this->request($url, $headers, $data, Requests::PUT, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a PATCH request
|
* Send a PATCH request
|
||||||
*
|
*
|
||||||
* Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
|
* Note: Unlike {@see \WpOrg\Requests\Session::post()} and {@see \WpOrg\Requests\Session::put()},
|
||||||
* specification recommends that should send an ETag
|
* `$headers` is required, as the specification recommends that should send an ETag
|
||||||
*
|
*
|
||||||
* @link https://tools.ietf.org/html/rfc5789
|
* @link https://tools.ietf.org/html/rfc5789
|
||||||
*/
|
*/
|
||||||
public function patch($url, $headers, $data = array(), $options = array()) {
|
public function patch($url, $headers, $data = [], $options = []) {
|
||||||
return $this->request($url, $headers, $data, Requests::PATCH, $options);
|
return $this->request($url, $headers, $data, Requests::PATCH, $options);
|
||||||
}
|
}
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
@ -190,18 +215,18 @@ class Requests_Session {
|
||||||
* This method initiates a request and sends it via a transport before
|
* This method initiates a request and sends it via a transport before
|
||||||
* parsing.
|
* parsing.
|
||||||
*
|
*
|
||||||
* @see Requests::request()
|
* @see \WpOrg\Requests\Requests::request()
|
||||||
*
|
|
||||||
* @throws Requests_Exception On invalid URLs (`nonhttp`)
|
|
||||||
*
|
*
|
||||||
* @param string $url URL to request
|
* @param string $url URL to request
|
||||||
* @param array $headers Extra headers to send with the request
|
* @param array $headers Extra headers to send with the request
|
||||||
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
|
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
|
||||||
* @param string $type HTTP request type (use Requests constants)
|
* @param string $type HTTP request type (use \WpOrg\Requests\Requests constants)
|
||||||
* @param array $options Options for the request (see {@see Requests::request})
|
* @param array $options Options for the request (see {@see \WpOrg\Requests\Requests::request()})
|
||||||
* @return Requests_Response
|
* @return \WpOrg\Requests\Response
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On invalid URLs (`nonhttp`)
|
||||||
*/
|
*/
|
||||||
public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) {
|
public function request($url, $headers = [], $data = [], $type = Requests::GET, $options = []) {
|
||||||
$request = $this->merge_request(compact('url', 'headers', 'data', 'options'));
|
$request = $this->merge_request(compact('url', 'headers', 'data', 'options'));
|
||||||
|
|
||||||
return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']);
|
return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']);
|
||||||
|
@ -210,13 +235,24 @@ class Requests_Session {
|
||||||
/**
|
/**
|
||||||
* Send multiple HTTP requests simultaneously
|
* Send multiple HTTP requests simultaneously
|
||||||
*
|
*
|
||||||
* @see Requests::request_multiple()
|
* @see \WpOrg\Requests\Requests::request_multiple()
|
||||||
*
|
*
|
||||||
* @param array $requests Requests data (see {@see Requests::request_multiple})
|
* @param array $requests Requests data (see {@see \WpOrg\Requests\Requests::request_multiple()})
|
||||||
* @param array $options Global and default options (see {@see Requests::request})
|
* @param array $options Global and default options (see {@see \WpOrg\Requests\Requests::request()})
|
||||||
* @return array Responses (either Requests_Response or a Requests_Exception object)
|
* @return array Responses (either \WpOrg\Requests\Response or a \WpOrg\Requests\Exception object)
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||||
*/
|
*/
|
||||||
public function request_multiple($requests, $options = array()) {
|
public function request_multiple($requests, $options = []) {
|
||||||
|
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($options) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($requests as $key => $request) {
|
foreach ($requests as $key => $request) {
|
||||||
$requests[$key] = $this->merge_request($request, false);
|
$requests[$key] = $this->merge_request($request, false);
|
||||||
}
|
}
|
||||||
|
@ -232,31 +268,31 @@ class Requests_Session {
|
||||||
/**
|
/**
|
||||||
* Merge a request's data with the default data
|
* Merge a request's data with the default data
|
||||||
*
|
*
|
||||||
* @param array $request Request data (same form as {@see request_multiple})
|
* @param array $request Request data (same form as {@see \WpOrg\Requests\Session::request_multiple()})
|
||||||
* @param boolean $merge_options Should we merge options as well?
|
* @param boolean $merge_options Should we merge options as well?
|
||||||
* @return array Request data
|
* @return array Request data
|
||||||
*/
|
*/
|
||||||
protected function merge_request($request, $merge_options = true) {
|
protected function merge_request($request, $merge_options = true) {
|
||||||
if ($this->url !== null) {
|
if ($this->url !== null) {
|
||||||
$request['url'] = Requests_IRI::absolutize($this->url, $request['url']);
|
$request['url'] = Iri::absolutize($this->url, $request['url']);
|
||||||
$request['url'] = $request['url']->uri;
|
$request['url'] = $request['url']->uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($request['headers'])) {
|
if (empty($request['headers'])) {
|
||||||
$request['headers'] = array();
|
$request['headers'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$request['headers'] = array_merge($this->headers, $request['headers']);
|
$request['headers'] = array_merge($this->headers, $request['headers']);
|
||||||
|
|
||||||
if (empty($request['data'])) {
|
if (empty($request['data'])) {
|
||||||
if (is_array($this->data)) {
|
if (is_array($this->data)) {
|
||||||
$request['data'] = $this->data;
|
$request['data'] = $this->data;
|
||||||
}
|
}
|
||||||
}
|
} elseif (is_array($request['data']) && is_array($this->data)) {
|
||||||
elseif (is_array($request['data']) && is_array($this->data)) {
|
|
||||||
$request['data'] = array_merge($this->data, $request['data']);
|
$request['data'] = array_merge($this->data, $request['data']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($merge_options !== false) {
|
if ($merge_options === true) {
|
||||||
$request['options'] = array_merge($this->options, $request['options']);
|
$request['options'] = array_merge($this->options, $request['options']);
|
||||||
|
|
||||||
// Disallow forcing the type, as that's a per request setting
|
// Disallow forcing the type, as that's a per request setting
|
|
@ -2,37 +2,49 @@
|
||||||
/**
|
/**
|
||||||
* SSL utilities for Requests
|
* SSL utilities for Requests
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSL utilities for Requests
|
* SSL utilities for Requests
|
||||||
*
|
*
|
||||||
* Collection of utilities for working with and verifying SSL certificates.
|
* Collection of utilities for working with and verifying SSL certificates.
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Utilities
|
||||||
* @subpackage Utilities
|
|
||||||
*/
|
*/
|
||||||
class Requests_SSL {
|
final class Ssl {
|
||||||
/**
|
/**
|
||||||
* Verify the certificate against common name and subject alternative names
|
* Verify the certificate against common name and subject alternative names
|
||||||
*
|
*
|
||||||
* Unfortunately, PHP doesn't check the certificate against the alternative
|
* Unfortunately, PHP doesn't check the certificate against the alternative
|
||||||
* names, leading things like 'https://www.github.com/' to be invalid.
|
* names, leading things like 'https://www.github.com/' to be invalid.
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
|
* @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
|
* @param string|Stringable $host Host name to verify against
|
||||||
* @param string $host Host name to verify against
|
|
||||||
* @param array $cert Certificate data from openssl_x509_parse()
|
* @param array $cert Certificate data from openssl_x509_parse()
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $host argument is not a string or a stringable object.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cert argument is not an array or array accessible.
|
||||||
*/
|
*/
|
||||||
public static function verify_certificate($host, $cert) {
|
public static function verify_certificate($host, $cert) {
|
||||||
|
if (InputValidator::is_string_or_stringable($host) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InputValidator::has_array_access($cert) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$cert', 'array|ArrayAccess', gettype($cert));
|
||||||
|
}
|
||||||
|
|
||||||
$has_dns_alt = false;
|
$has_dns_alt = false;
|
||||||
|
|
||||||
// Check the subjectAltName
|
// Check the subjectAltName
|
||||||
if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) {
|
if (!empty($cert['extensions']['subjectAltName'])) {
|
||||||
$altnames = explode(',', $cert['extensions']['subjectAltName']);
|
$altnames = explode(',', $cert['extensions']['subjectAltName']);
|
||||||
foreach ($altnames as $altname) {
|
foreach ($altnames as $altname) {
|
||||||
$altname = trim($altname);
|
$altname = trim($altname);
|
||||||
|
@ -50,15 +62,17 @@ class Requests_SSL {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($has_dns_alt === true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to checking the common name if we didn't get any dNSName
|
// Fall back to checking the common name if we didn't get any dNSName
|
||||||
// alt names, as per RFC2818
|
// alt names, as per RFC2818
|
||||||
if (!$has_dns_alt && !empty($cert['subject']['CN'])) {
|
if (!empty($cert['subject']['CN'])) {
|
||||||
// Check for a match
|
// Check for a match
|
||||||
if (self::match_domain($host, $cert['subject']['CN']) === true) {
|
return (self::match_domain($host, $cert['subject']['CN']) === true);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -77,11 +91,29 @@ class Requests_SSL {
|
||||||
* character to be the full first component; that is, with the exclusion of
|
* character to be the full first component; that is, with the exclusion of
|
||||||
* the third rule.
|
* the third rule.
|
||||||
*
|
*
|
||||||
* @param string $reference Reference dNSName
|
* @param string|Stringable $reference Reference dNSName
|
||||||
* @return boolean Is the name valid?
|
* @return boolean Is the name valid?
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
|
||||||
*/
|
*/
|
||||||
public static function verify_reference_name($reference) {
|
public static function verify_reference_name($reference) {
|
||||||
|
if (InputValidator::is_string_or_stringable($reference) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$reference', 'string|Stringable', gettype($reference));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reference === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('`\s`', $reference) > 0) {
|
||||||
|
// Whitespace detected. This can never be a dNSName.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$parts = explode('.', $reference);
|
$parts = explode('.', $reference);
|
||||||
|
if ($parts !== array_filter($parts)) {
|
||||||
|
// DNSName cannot contain two dots next to each other.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check the first part of the name
|
// Check the first part of the name
|
||||||
$first = array_shift($parts);
|
$first = array_shift($parts);
|
||||||
|
@ -112,29 +144,35 @@ class Requests_SSL {
|
||||||
/**
|
/**
|
||||||
* Match a hostname against a dNSName reference
|
* Match a hostname against a dNSName reference
|
||||||
*
|
*
|
||||||
* @param string $host Requested host
|
* @param string|Stringable $host Requested host
|
||||||
* @param string $reference dNSName to match against
|
* @param string|Stringable $reference dNSName to match against
|
||||||
* @return boolean Does the domain match?
|
* @return boolean Does the domain match?
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When either of the passed arguments is not a string or a stringable object.
|
||||||
*/
|
*/
|
||||||
public static function match_domain($host, $reference) {
|
public static function match_domain($host, $reference) {
|
||||||
|
if (InputValidator::is_string_or_stringable($host) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the reference is blocklisted first
|
// Check if the reference is blocklisted first
|
||||||
if (self::verify_reference_name($reference) !== true) {
|
if (self::verify_reference_name($reference) !== true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a direct match
|
// Check for a direct match
|
||||||
if ($host === $reference) {
|
if ((string) $host === (string) $reference) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the valid wildcard match if the host is not an IP address
|
// Calculate the valid wildcard match if the host is not an IP address
|
||||||
// Also validates that the host has 3 parts or more, as per Firefox's
|
// Also validates that the host has 3 parts or more, as per Firefox's ruleset,
|
||||||
// ruleset.
|
// as a wildcard reference is only allowed with 3 parts or more, so the
|
||||||
|
// comparison will never match if host doesn't contain 3 parts or more as well.
|
||||||
if (ip2long($host) === false) {
|
if (ip2long($host) === false) {
|
||||||
$parts = explode('.', $host);
|
$parts = explode('.', $host);
|
||||||
$parts[0] = '*';
|
$parts[0] = '*';
|
||||||
$wildcard = implode('.', $parts);
|
$wildcard = implode('.', $parts);
|
||||||
if ($wildcard === $reference) {
|
if ($wildcard === (string) $reference) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Base HTTP transport
|
||||||
|
*
|
||||||
|
* @package Requests\Transport
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base HTTP transport
|
||||||
|
*
|
||||||
|
* @package Requests\Transport
|
||||||
|
*/
|
||||||
|
interface Transport {
|
||||||
|
/**
|
||||||
|
* Perform a request
|
||||||
|
*
|
||||||
|
* @param string $url URL to request
|
||||||
|
* @param array $headers Associative array of request headers
|
||||||
|
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
||||||
|
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
|
* @return string Raw HTTP result
|
||||||
|
*/
|
||||||
|
public function request($url, $headers = [], $data = [], $options = []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send multiple requests simultaneously
|
||||||
|
*
|
||||||
|
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see \WpOrg\Requests\Transport::request()}
|
||||||
|
* @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
|
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
|
||||||
|
*/
|
||||||
|
public function request_multiple($requests, $options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Self-test whether the transport can be used.
|
||||||
|
*
|
||||||
|
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
|
||||||
|
*
|
||||||
|
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
|
||||||
|
* @return bool Whether the transport can be used.
|
||||||
|
*/
|
||||||
|
public static function test($capabilities = []);
|
||||||
|
}
|
|
@ -2,17 +2,27 @@
|
||||||
/**
|
/**
|
||||||
* cURL HTTP transport
|
* cURL HTTP transport
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Transport
|
||||||
* @subpackage Transport
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Transport;
|
||||||
|
|
||||||
|
use RecursiveArrayIterator;
|
||||||
|
use RecursiveIteratorIterator;
|
||||||
|
use WpOrg\Requests\Capability;
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Exception\Transport\Curl as CurlException;
|
||||||
|
use WpOrg\Requests\Requests;
|
||||||
|
use WpOrg\Requests\Transport;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cURL HTTP transport
|
* cURL HTTP transport
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Transport
|
||||||
* @subpackage Transport
|
|
||||||
*/
|
*/
|
||||||
class Requests_Transport_cURL implements Requests_Transport {
|
final class Curl implements Transport {
|
||||||
const CURL_7_10_5 = 0x070A05;
|
const CURL_7_10_5 = 0x070A05;
|
||||||
const CURL_7_16_2 = 0x071002;
|
const CURL_7_16_2 = 0x071002;
|
||||||
|
|
||||||
|
@ -33,7 +43,7 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
/**
|
/**
|
||||||
* Information on the current request
|
* Information on the current request
|
||||||
*
|
*
|
||||||
* @var array cURL information array, see {@see https://secure.php.net/curl_getinfo}
|
* @var array cURL information array, see {@link https://www.php.net/curl_getinfo}
|
||||||
*/
|
*/
|
||||||
public $info;
|
public $info;
|
||||||
|
|
||||||
|
@ -47,44 +57,44 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
/**
|
/**
|
||||||
* cURL handle
|
* cURL handle
|
||||||
*
|
*
|
||||||
* @var resource
|
* @var resource|\CurlHandle Resource in PHP < 8.0, Instance of CurlHandle in PHP >= 8.0.
|
||||||
*/
|
*/
|
||||||
protected $handle;
|
private $handle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook dispatcher instance
|
* Hook dispatcher instance
|
||||||
*
|
*
|
||||||
* @var Requests_Hooks
|
* @var \WpOrg\Requests\Hooks
|
||||||
*/
|
*/
|
||||||
protected $hooks;
|
private $hooks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Have we finished the headers yet?
|
* Have we finished the headers yet?
|
||||||
*
|
*
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
protected $done_headers = false;
|
private $done_headers = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If streaming to a file, keep the file pointer
|
* If streaming to a file, keep the file pointer
|
||||||
*
|
*
|
||||||
* @var resource
|
* @var resource
|
||||||
*/
|
*/
|
||||||
protected $stream_handle;
|
private $stream_handle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many bytes are in the response body?
|
* How many bytes are in the response body?
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $response_bytes;
|
private $response_bytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What's the maximum number of bytes we should keep?
|
* What's the maximum number of bytes we should keep?
|
||||||
*
|
*
|
||||||
* @var int|bool Byte count, or false if no limit.
|
* @var int|bool Byte count, or false if no limit.
|
||||||
*/
|
*/
|
||||||
protected $response_byte_limit;
|
private $response_byte_limit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -99,10 +109,12 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
if ($this->version >= self::CURL_7_10_5) {
|
if ($this->version >= self::CURL_7_10_5) {
|
||||||
curl_setopt($this->handle, CURLOPT_ENCODING, '');
|
curl_setopt($this->handle, CURLOPT_ENCODING, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined('CURLOPT_PROTOCOLS')) {
|
if (defined('CURLOPT_PROTOCOLS')) {
|
||||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound
|
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound
|
||||||
curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
|
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
|
||||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound
|
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound
|
||||||
curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||||
|
@ -121,23 +133,52 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
/**
|
/**
|
||||||
* Perform a request
|
* Perform a request
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception On a cURL error (`curlerror`)
|
* @param string|Stringable $url URL to request
|
||||||
*
|
|
||||||
* @param string $url URL to request
|
|
||||||
* @param array $headers Associative array of request headers
|
* @param array $headers Associative array of request headers
|
||||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
||||||
* @param array $options Request options, see {@see Requests::response()} for documentation
|
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
* @return string Raw HTTP result
|
* @return string Raw HTTP result
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception On a cURL error (`curlerror`)
|
||||||
*/
|
*/
|
||||||
public function request($url, $headers = array(), $data = array(), $options = array()) {
|
public function request($url, $headers = [], $data = [], $options = []) {
|
||||||
|
if (InputValidator::is_string_or_stringable($url) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($headers) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($data) && !is_string($data)) {
|
||||||
|
if ($data === null) {
|
||||||
|
$data = '';
|
||||||
|
} else {
|
||||||
|
throw InvalidArgument::create(3, '$data', 'array|string', gettype($data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($options) === false) {
|
||||||
|
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
|
||||||
|
}
|
||||||
|
|
||||||
$this->hooks = $options['hooks'];
|
$this->hooks = $options['hooks'];
|
||||||
|
|
||||||
$this->setup_handle($url, $headers, $data, $options);
|
$this->setup_handle($url, $headers, $data, $options);
|
||||||
|
|
||||||
$options['hooks']->dispatch('curl.before_send', array(&$this->handle));
|
$options['hooks']->dispatch('curl.before_send', [&$this->handle]);
|
||||||
|
|
||||||
if ($options['filename'] !== false) {
|
if ($options['filename'] !== false) {
|
||||||
$this->stream_handle = fopen($options['filename'], 'wb');
|
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
|
||||||
|
$this->stream_handle = @fopen($options['filename'], 'wb');
|
||||||
|
if ($this->stream_handle === false) {
|
||||||
|
$error = error_get_last();
|
||||||
|
throw new Exception($error['message'], 'fopen');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->response_data = '';
|
$this->response_data = '';
|
||||||
|
@ -151,8 +192,7 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
if ($options['verify'] === false) {
|
if ($options['verify'] === false) {
|
||||||
curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
|
curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0);
|
curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0);
|
||||||
}
|
} elseif (is_string($options['verify'])) {
|
||||||
elseif (is_string($options['verify'])) {
|
|
||||||
curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']);
|
curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,9 +204,9 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
curl_exec($this->handle);
|
curl_exec($this->handle);
|
||||||
$response = $this->response_data;
|
$response = $this->response_data;
|
||||||
|
|
||||||
$options['hooks']->dispatch('curl.after_send', array());
|
$options['hooks']->dispatch('curl.after_send', []);
|
||||||
|
|
||||||
if (curl_errno($this->handle) === 23 || curl_errno($this->handle) === 61) {
|
if (curl_errno($this->handle) === CURLE_WRITE_ERROR || curl_errno($this->handle) === CURLE_BAD_CONTENT_ENCODING) {
|
||||||
// Reset encoding and try again
|
// Reset encoding and try again
|
||||||
curl_setopt($this->handle, CURLOPT_ENCODING, 'none');
|
curl_setopt($this->handle, CURLOPT_ENCODING, 'none');
|
||||||
|
|
||||||
|
@ -179,7 +219,7 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
$this->process_response($response, $options);
|
$this->process_response($response, $options);
|
||||||
|
|
||||||
// Need to remove the $this reference from the curl handle.
|
// Need to remove the $this reference from the curl handle.
|
||||||
// Otherwise Requests_Transport_cURL wont be garbage collected and the curl_close() will never be called.
|
// Otherwise \WpOrg\Requests\Transport\Curl won't be garbage collected and the curl_close() will never be called.
|
||||||
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null);
|
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null);
|
||||||
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null);
|
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null);
|
||||||
|
|
||||||
|
@ -191,41 +231,51 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
*
|
*
|
||||||
* @param array $requests Request data
|
* @param array $requests Request data
|
||||||
* @param array $options Global options
|
* @param array $options Global options
|
||||||
* @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
|
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||||
*/
|
*/
|
||||||
public function request_multiple($requests, $options) {
|
public function request_multiple($requests, $options) {
|
||||||
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
|
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
|
||||||
if (empty($requests)) {
|
if (empty($requests)) {
|
||||||
return array();
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($options) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
|
||||||
}
|
}
|
||||||
|
|
||||||
$multihandle = curl_multi_init();
|
$multihandle = curl_multi_init();
|
||||||
$subrequests = array();
|
$subrequests = [];
|
||||||
$subhandles = array();
|
$subhandles = [];
|
||||||
|
|
||||||
$class = get_class($this);
|
$class = get_class($this);
|
||||||
foreach ($requests as $id => $request) {
|
foreach ($requests as $id => $request) {
|
||||||
$subrequests[$id] = new $class();
|
$subrequests[$id] = new $class();
|
||||||
$subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']);
|
$subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']);
|
||||||
$request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id]));
|
$request['options']['hooks']->dispatch('curl.before_multi_add', [&$subhandles[$id]]);
|
||||||
curl_multi_add_handle($multihandle, $subhandles[$id]);
|
curl_multi_add_handle($multihandle, $subhandles[$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$completed = 0;
|
$completed = 0;
|
||||||
$responses = array();
|
$responses = [];
|
||||||
$subrequestcount = count($subrequests);
|
$subrequestcount = count($subrequests);
|
||||||
|
|
||||||
$request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle));
|
$request['options']['hooks']->dispatch('curl.before_multi_exec', [&$multihandle]);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$active = 0;
|
$active = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$status = curl_multi_exec($multihandle, $active);
|
$status = curl_multi_exec($multihandle, $active);
|
||||||
}
|
} while ($status === CURLM_CALL_MULTI_PERFORM);
|
||||||
while ($status === CURLM_CALL_MULTI_PERFORM);
|
|
||||||
|
|
||||||
$to_process = array();
|
$to_process = [];
|
||||||
|
|
||||||
// Read the information as needed
|
// Read the information as needed
|
||||||
while ($done = curl_multi_info_read($multihandle)) {
|
while ($done = curl_multi_info_read($multihandle)) {
|
||||||
|
@ -241,33 +291,32 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
if ($done['result'] !== CURLE_OK) {
|
if ($done['result'] !== CURLE_OK) {
|
||||||
//get error string for handle.
|
//get error string for handle.
|
||||||
$reason = curl_error($done['handle']);
|
$reason = curl_error($done['handle']);
|
||||||
$exception = new Requests_Exception_Transport_cURL(
|
$exception = new CurlException(
|
||||||
$reason,
|
$reason,
|
||||||
Requests_Exception_Transport_cURL::EASY,
|
CurlException::EASY,
|
||||||
$done['handle'],
|
$done['handle'],
|
||||||
$done['result']
|
$done['result']
|
||||||
);
|
);
|
||||||
$responses[$key] = $exception;
|
$responses[$key] = $exception;
|
||||||
$options['hooks']->dispatch('transport.internal.parse_error', array(&$responses[$key], $requests[$key]));
|
$options['hooks']->dispatch('transport.internal.parse_error', [&$responses[$key], $requests[$key]]);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options);
|
$responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options);
|
||||||
|
|
||||||
$options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key]));
|
$options['hooks']->dispatch('transport.internal.parse_response', [&$responses[$key], $requests[$key]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_multi_remove_handle($multihandle, $done['handle']);
|
curl_multi_remove_handle($multihandle, $done['handle']);
|
||||||
curl_close($done['handle']);
|
curl_close($done['handle']);
|
||||||
|
|
||||||
if (!is_string($responses[$key])) {
|
if (!is_string($responses[$key])) {
|
||||||
$options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key));
|
$options['hooks']->dispatch('multiple.request.complete', [&$responses[$key], $key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$completed++;
|
$completed++;
|
||||||
}
|
}
|
||||||
}
|
} while ($active || $completed < $subrequestcount);
|
||||||
while ($active || $completed < $subrequestcount);
|
|
||||||
|
|
||||||
$request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle));
|
$request['options']['hooks']->dispatch('curl.after_multi_exec', [&$multihandle]);
|
||||||
|
|
||||||
curl_multi_close($multihandle);
|
curl_multi_close($multihandle);
|
||||||
|
|
||||||
|
@ -280,8 +329,8 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
* @param string $url URL to request
|
* @param string $url URL to request
|
||||||
* @param array $headers Associative array of request headers
|
* @param array $headers Associative array of request headers
|
||||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
||||||
* @param array $options Request options, see {@see Requests::response()} for documentation
|
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
* @return resource Subrequest's cURL handle
|
* @return resource|\CurlHandle Subrequest's cURL handle
|
||||||
*/
|
*/
|
||||||
public function &get_subrequest_handle($url, $headers, $data, $options) {
|
public function &get_subrequest_handle($url, $headers, $data, $options) {
|
||||||
$this->setup_handle($url, $headers, $data, $options);
|
$this->setup_handle($url, $headers, $data, $options);
|
||||||
|
@ -296,6 +345,7 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
if ($options['max_bytes'] !== false) {
|
if ($options['max_bytes'] !== false) {
|
||||||
$this->response_byte_limit = $options['max_bytes'];
|
$this->response_byte_limit = $options['max_bytes'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->hooks = $options['hooks'];
|
$this->hooks = $options['hooks'];
|
||||||
|
|
||||||
return $this->handle;
|
return $this->handle;
|
||||||
|
@ -307,10 +357,10 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
* @param string $url URL to request
|
* @param string $url URL to request
|
||||||
* @param array $headers Associative array of request headers
|
* @param array $headers Associative array of request headers
|
||||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
||||||
* @param array $options Request options, see {@see Requests::response()} for documentation
|
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
*/
|
*/
|
||||||
protected function setup_handle($url, $headers, $data, $options) {
|
private function setup_handle($url, $headers, $data, $options) {
|
||||||
$options['hooks']->dispatch('curl.before_request', array(&$this->handle));
|
$options['hooks']->dispatch('curl.before_request', [&$this->handle]);
|
||||||
|
|
||||||
// Force closing the connection for old versions of cURL (<7.22).
|
// Force closing the connection for old versions of cURL (<7.22).
|
||||||
if (!isset($headers['Connection'])) {
|
if (!isset($headers['Connection'])) {
|
||||||
|
@ -340,9 +390,8 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
if ($data_format === 'query') {
|
if ($data_format === 'query') {
|
||||||
$url = self::format_get($url, $data);
|
$url = self::format_get($url, $data);
|
||||||
$data = '';
|
$data = '';
|
||||||
}
|
} elseif (!is_string($data)) {
|
||||||
elseif (!is_string($data)) {
|
$data = http_build_query($data, '', '&');
|
||||||
$data = http_build_query($data, null, '&');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,35 +428,33 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
|
|
||||||
if (is_int($timeout) || $this->version < self::CURL_7_16_2) {
|
if (is_int($timeout) || $this->version < self::CURL_7_16_2) {
|
||||||
curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout));
|
curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_timeout_msFound
|
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_timeout_msFound
|
||||||
curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000));
|
curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) {
|
if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) {
|
||||||
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout']));
|
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout']));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound
|
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound
|
||||||
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000));
|
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_setopt($this->handle, CURLOPT_URL, $url);
|
curl_setopt($this->handle, CURLOPT_URL, $url);
|
||||||
curl_setopt($this->handle, CURLOPT_REFERER, $url);
|
|
||||||
curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']);
|
curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']);
|
||||||
if (!empty($headers)) {
|
if (!empty($headers)) {
|
||||||
curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers);
|
curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($options['protocol_version'] === 1.1) {
|
if ($options['protocol_version'] === 1.1) {
|
||||||
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($options['blocking'] === true) {
|
if ($options['blocking'] === true) {
|
||||||
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array($this, 'stream_headers'));
|
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, [$this, 'stream_headers']);
|
||||||
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, array($this, 'stream_body'));
|
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, [$this, 'stream_body']);
|
||||||
curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE);
|
curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,19 +465,19 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
* @param string $response Response data from the body
|
* @param string $response Response data from the body
|
||||||
* @param array $options Request options
|
* @param array $options Request options
|
||||||
* @return string|false HTTP response data including headers. False if non-blocking.
|
* @return string|false HTTP response data including headers. False if non-blocking.
|
||||||
* @throws Requests_Exception
|
* @throws \WpOrg\Requests\Exception
|
||||||
*/
|
*/
|
||||||
public function process_response($response, $options) {
|
public function process_response($response, $options) {
|
||||||
if ($options['blocking'] === false) {
|
if ($options['blocking'] === false) {
|
||||||
$fake_headers = '';
|
$fake_headers = '';
|
||||||
$options['hooks']->dispatch('curl.after_request', array(&$fake_headers));
|
$options['hooks']->dispatch('curl.after_request', [&$fake_headers]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($options['filename'] !== false && $this->stream_handle) {
|
if ($options['filename'] !== false && $this->stream_handle) {
|
||||||
fclose($this->stream_handle);
|
fclose($this->stream_handle);
|
||||||
$this->headers = trim($this->headers);
|
$this->headers = trim($this->headers);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$this->headers .= $response;
|
$this->headers .= $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,18 +487,19 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
curl_errno($this->handle),
|
curl_errno($this->handle),
|
||||||
curl_error($this->handle)
|
curl_error($this->handle)
|
||||||
);
|
);
|
||||||
throw new Requests_Exception($error, 'curlerror', $this->handle);
|
throw new Exception($error, 'curlerror', $this->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->info = curl_getinfo($this->handle);
|
$this->info = curl_getinfo($this->handle);
|
||||||
|
|
||||||
$options['hooks']->dispatch('curl.after_request', array(&$this->headers, &$this->info));
|
$options['hooks']->dispatch('curl.after_request', [&$this->headers, &$this->info]);
|
||||||
return $this->headers;
|
return $this->headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect the headers as they are received
|
* Collect the headers as they are received
|
||||||
*
|
*
|
||||||
* @param resource $handle cURL resource
|
* @param resource|\CurlHandle $handle cURL handle
|
||||||
* @param string $headers Header string
|
* @param string $headers Header string
|
||||||
* @return integer Length of provided header
|
* @return integer Length of provided header
|
||||||
*/
|
*/
|
||||||
|
@ -463,11 +511,13 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
$this->headers = '';
|
$this->headers = '';
|
||||||
$this->done_headers = false;
|
$this->done_headers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->headers .= $headers;
|
$this->headers .= $headers;
|
||||||
|
|
||||||
if ($headers === "\r\n") {
|
if ($headers === "\r\n") {
|
||||||
$this->done_headers = true;
|
$this->done_headers = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return strlen($headers);
|
return strlen($headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,12 +526,12 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
*
|
*
|
||||||
* @since 1.6.1
|
* @since 1.6.1
|
||||||
*
|
*
|
||||||
* @param resource $handle cURL resource
|
* @param resource|\CurlHandle $handle cURL handle
|
||||||
* @param string $data Body data
|
* @param string $data Body data
|
||||||
* @return integer Length of provided data
|
* @return integer Length of provided data
|
||||||
*/
|
*/
|
||||||
public function stream_body($handle, $data) {
|
public function stream_body($handle, $data) {
|
||||||
$this->hooks->dispatch('request.progress', array($data, $this->response_bytes, $this->response_byte_limit));
|
$this->hooks->dispatch('request.progress', [$data, $this->response_bytes, $this->response_byte_limit]);
|
||||||
$data_length = strlen($data);
|
$data_length = strlen($data);
|
||||||
|
|
||||||
// Are we limiting the response size?
|
// Are we limiting the response size?
|
||||||
|
@ -500,8 +550,7 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
|
|
||||||
if ($this->stream_handle) {
|
if ($this->stream_handle) {
|
||||||
fwrite($this->stream_handle, $data);
|
fwrite($this->stream_handle, $data);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$this->response_data .= $data;
|
$this->response_data .= $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,46 +562,48 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
* Format a URL given GET data
|
* Format a URL given GET data
|
||||||
*
|
*
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query}
|
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
|
||||||
* @return string URL with data
|
* @return string URL with data
|
||||||
*/
|
*/
|
||||||
protected static function format_get($url, $data) {
|
private static function format_get($url, $data) {
|
||||||
if (!empty($data)) {
|
if (!empty($data)) {
|
||||||
$query = '';
|
$query = '';
|
||||||
$url_parts = parse_url($url);
|
$url_parts = parse_url($url);
|
||||||
if (empty($url_parts['query'])) {
|
if (empty($url_parts['query'])) {
|
||||||
$url_parts['query'] = '';
|
$url_parts['query'] = '';
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$query = $url_parts['query'];
|
$query = $url_parts['query'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$query .= '&' . http_build_query($data, null, '&');
|
$query .= '&' . http_build_query($data, '', '&');
|
||||||
$query = trim($query, '&');
|
$query = trim($query, '&');
|
||||||
|
|
||||||
if (empty($url_parts['query'])) {
|
if (empty($url_parts['query'])) {
|
||||||
$url .= '?' . $query;
|
$url .= '?' . $query;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$url = str_replace($url_parts['query'], $query, $url);
|
$url = str_replace($url_parts['query'], $query, $url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this transport is valid
|
* Self-test whether the transport can be used.
|
||||||
|
*
|
||||||
|
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
|
||||||
*
|
*
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return boolean True if the transport is valid, false otherwise.
|
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
|
||||||
|
* @return bool Whether the transport can be used.
|
||||||
*/
|
*/
|
||||||
public static function test($capabilities = array()) {
|
public static function test($capabilities = []) {
|
||||||
if (!function_exists('curl_init') || !function_exists('curl_exec')) {
|
if (!function_exists('curl_init') || !function_exists('curl_exec')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If needed, check that our installed curl version supports SSL
|
// If needed, check that our installed curl version supports SSL
|
||||||
if (isset($capabilities['ssl']) && $capabilities['ssl']) {
|
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
|
||||||
$curl_version = curl_version();
|
$curl_version = curl_version();
|
||||||
if (!(CURL_VERSION_SSL & $curl_version['features'])) {
|
if (!(CURL_VERSION_SSL & $curl_version['features'])) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -568,7 +619,7 @@ class Requests_Transport_cURL implements Requests_Transport {
|
||||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD.
|
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD.
|
||||||
* @return string The "Expect" header.
|
* @return string The "Expect" header.
|
||||||
*/
|
*/
|
||||||
protected function get_expect_header($data) {
|
private function get_expect_header($data) {
|
||||||
if (!is_array($data)) {
|
if (!is_array($data)) {
|
||||||
return strlen((string) $data) >= 1048576 ? '100-Continue' : '';
|
return strlen((string) $data) >= 1048576 ? '100-Continue' : '';
|
||||||
}
|
}
|
|
@ -2,17 +2,27 @@
|
||||||
/**
|
/**
|
||||||
* fsockopen HTTP transport
|
* fsockopen HTTP transport
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Transport
|
||||||
* @subpackage Transport
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Transport;
|
||||||
|
|
||||||
|
use WpOrg\Requests\Capability;
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Port;
|
||||||
|
use WpOrg\Requests\Requests;
|
||||||
|
use WpOrg\Requests\Ssl;
|
||||||
|
use WpOrg\Requests\Transport;
|
||||||
|
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fsockopen HTTP transport
|
* fsockopen HTTP transport
|
||||||
*
|
*
|
||||||
* @package Requests
|
* @package Requests\Transport
|
||||||
* @subpackage Transport
|
|
||||||
*/
|
*/
|
||||||
class Requests_Transport_fsockopen implements Requests_Transport {
|
final class Fsockopen implements Transport {
|
||||||
/**
|
/**
|
||||||
* Second to microsecond conversion
|
* Second to microsecond conversion
|
||||||
*
|
*
|
||||||
|
@ -30,7 +40,7 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
/**
|
/**
|
||||||
* Stream metadata
|
* Stream metadata
|
||||||
*
|
*
|
||||||
* @var array Associative array of properties, see {@see https://secure.php.net/stream_get_meta_data}
|
* @var array Associative array of properties, see {@link https://www.php.net/stream_get_meta_data}
|
||||||
*/
|
*/
|
||||||
public $info;
|
public $info;
|
||||||
|
|
||||||
|
@ -39,45 +49,70 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
*
|
*
|
||||||
* @var int|bool Byte count, or false if no limit.
|
* @var int|bool Byte count, or false if no limit.
|
||||||
*/
|
*/
|
||||||
protected $max_bytes = false;
|
private $max_bytes = false;
|
||||||
|
|
||||||
protected $connect_error = '';
|
private $connect_error = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a request
|
* Perform a request
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception On failure to connect to socket (`fsockopenerror`)
|
* @param string|Stringable $url URL to request
|
||||||
* @throws Requests_Exception On socket timeout (`timeout`)
|
|
||||||
*
|
|
||||||
* @param string $url URL to request
|
|
||||||
* @param array $headers Associative array of request headers
|
* @param array $headers Associative array of request headers
|
||||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
||||||
* @param array $options Request options, see {@see Requests::response()} for documentation
|
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
* @return string Raw HTTP result
|
* @return string Raw HTTP result
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||||
|
* @throws \WpOrg\Requests\Exception On failure to connect to socket (`fsockopenerror`)
|
||||||
|
* @throws \WpOrg\Requests\Exception On socket timeout (`timeout`)
|
||||||
*/
|
*/
|
||||||
public function request($url, $headers = array(), $data = array(), $options = array()) {
|
public function request($url, $headers = [], $data = [], $options = []) {
|
||||||
|
if (InputValidator::is_string_or_stringable($url) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($headers) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($data) && !is_string($data)) {
|
||||||
|
if ($data === null) {
|
||||||
|
$data = '';
|
||||||
|
} else {
|
||||||
|
throw InvalidArgument::create(3, '$data', 'array|string', gettype($data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($options) === false) {
|
||||||
|
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
|
||||||
|
}
|
||||||
|
|
||||||
$options['hooks']->dispatch('fsockopen.before_request');
|
$options['hooks']->dispatch('fsockopen.before_request');
|
||||||
|
|
||||||
$url_parts = parse_url($url);
|
$url_parts = parse_url($url);
|
||||||
if (empty($url_parts)) {
|
if (empty($url_parts)) {
|
||||||
throw new Requests_Exception('Invalid URL.', 'invalidurl', $url);
|
throw new Exception('Invalid URL.', 'invalidurl', $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = $url_parts['host'];
|
$host = $url_parts['host'];
|
||||||
$context = stream_context_create();
|
$context = stream_context_create();
|
||||||
$verifyname = false;
|
$verifyname = false;
|
||||||
$case_insensitive_headers = new Requests_Utility_CaseInsensitiveDictionary($headers);
|
$case_insensitive_headers = new CaseInsensitiveDictionary($headers);
|
||||||
|
|
||||||
// HTTPS support
|
// HTTPS support
|
||||||
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
|
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
|
||||||
$remote_socket = 'ssl://' . $host;
|
$remote_socket = 'ssl://' . $host;
|
||||||
if (!isset($url_parts['port'])) {
|
if (!isset($url_parts['port'])) {
|
||||||
$url_parts['port'] = 443;
|
$url_parts['port'] = Port::HTTPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
$context_options = array(
|
$context_options = [
|
||||||
'verify_peer' => true,
|
'verify_peer' => true,
|
||||||
'capture_peer_cert' => true,
|
'capture_peer_cert' => true,
|
||||||
);
|
];
|
||||||
$verifyname = true;
|
$verifyname = true;
|
||||||
|
|
||||||
// SNI, if enabled (OpenSSL >=0.9.8j)
|
// SNI, if enabled (OpenSSL >=0.9.8j)
|
||||||
|
@ -94,8 +129,7 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
$context_options['verify_peer'] = false;
|
$context_options['verify_peer'] = false;
|
||||||
$context_options['verify_peer_name'] = false;
|
$context_options['verify_peer_name'] = false;
|
||||||
$verifyname = false;
|
$verifyname = false;
|
||||||
}
|
} elseif (is_string($options['verify'])) {
|
||||||
elseif (is_string($options['verify'])) {
|
|
||||||
$context_options['cafile'] = $options['verify'];
|
$context_options['cafile'] = $options['verify'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,39 +139,39 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
$verifyname = false;
|
$verifyname = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_context_set_option($context, array('ssl' => $context_options));
|
stream_context_set_option($context, ['ssl' => $context_options]);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$remote_socket = 'tcp://' . $host;
|
$remote_socket = 'tcp://' . $host;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->max_bytes = $options['max_bytes'];
|
$this->max_bytes = $options['max_bytes'];
|
||||||
|
|
||||||
if (!isset($url_parts['port'])) {
|
if (!isset($url_parts['port'])) {
|
||||||
$url_parts['port'] = 80;
|
$url_parts['port'] = Port::HTTP;
|
||||||
}
|
}
|
||||||
|
|
||||||
$remote_socket .= ':' . $url_parts['port'];
|
$remote_socket .= ':' . $url_parts['port'];
|
||||||
|
|
||||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
|
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
|
||||||
set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE);
|
set_error_handler([$this, 'connect_error_handler'], E_WARNING | E_NOTICE);
|
||||||
|
|
||||||
$options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket));
|
$options['hooks']->dispatch('fsockopen.remote_socket', [&$remote_socket]);
|
||||||
|
|
||||||
$socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context);
|
$socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context);
|
||||||
|
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
|
if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
|
||||||
throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
|
throw new Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$socket) {
|
if (!$socket) {
|
||||||
if ($errno === 0) {
|
if ($errno === 0) {
|
||||||
// Connection issue
|
// Connection issue
|
||||||
throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
|
throw new Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Requests_Exception($errstr, 'fsockopenerror', null, $errno);
|
throw new Exception($errstr, 'fsockopenerror', null, $errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data_format = $options['data_format'];
|
$data_format = $options['data_format'];
|
||||||
|
@ -145,12 +179,11 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
if ($data_format === 'query') {
|
if ($data_format === 'query') {
|
||||||
$path = self::format_get($url_parts, $data);
|
$path = self::format_get($url_parts, $data);
|
||||||
$data = '';
|
$data = '';
|
||||||
}
|
} else {
|
||||||
else {
|
$path = self::format_get($url_parts, []);
|
||||||
$path = self::format_get($url_parts, array());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));
|
$options['hooks']->dispatch('fsockopen.remote_host_path', [&$path, $url]);
|
||||||
|
|
||||||
$request_body = '';
|
$request_body = '';
|
||||||
$out = sprintf("%s %s HTTP/%.1F\r\n", $options['type'], $path, $options['protocol_version']);
|
$out = sprintf("%s %s HTTP/%.1F\r\n", $options['type'], $path, $options['protocol_version']);
|
||||||
|
@ -158,8 +191,7 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
if ($options['type'] !== Requests::TRACE) {
|
if ($options['type'] !== Requests::TRACE) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
$request_body = http_build_query($data, '', '&');
|
$request_body = http_build_query($data, '', '&');
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$request_body = $data;
|
$request_body = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,11 +209,13 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($case_insensitive_headers['Host'])) {
|
if (!isset($case_insensitive_headers['Host'])) {
|
||||||
$out .= sprintf('Host: %s', $url_parts['host']);
|
$out .= sprintf('Host: %s', $url_parts['host']);
|
||||||
|
$scheme_lower = strtolower($url_parts['scheme']);
|
||||||
|
|
||||||
if ((strtolower($url_parts['scheme']) === 'http' && $url_parts['port'] !== 80) || (strtolower($url_parts['scheme']) === 'https' && $url_parts['port'] !== 443)) {
|
if (($scheme_lower === 'http' && $url_parts['port'] !== Port::HTTP) || ($scheme_lower === 'https' && $url_parts['port'] !== Port::HTTPS)) {
|
||||||
$out .= ':' . $url_parts['port'];
|
$out .= ':' . $url_parts['port'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$out .= "\r\n";
|
$out .= "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +234,7 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
$out .= implode("\r\n", $headers) . "\r\n";
|
$out .= implode("\r\n", $headers) . "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$options['hooks']->dispatch('fsockopen.after_headers', array(&$out));
|
$options['hooks']->dispatch('fsockopen.after_headers', [&$out]);
|
||||||
|
|
||||||
if (substr($out, -2) !== "\r\n") {
|
if (substr($out, -2) !== "\r\n") {
|
||||||
$out .= "\r\n";
|
$out .= "\r\n";
|
||||||
|
@ -212,25 +246,25 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
|
|
||||||
$out .= "\r\n" . $request_body;
|
$out .= "\r\n" . $request_body;
|
||||||
|
|
||||||
$options['hooks']->dispatch('fsockopen.before_send', array(&$out));
|
$options['hooks']->dispatch('fsockopen.before_send', [&$out]);
|
||||||
|
|
||||||
fwrite($socket, $out);
|
fwrite($socket, $out);
|
||||||
$options['hooks']->dispatch('fsockopen.after_send', array($out));
|
$options['hooks']->dispatch('fsockopen.after_send', [$out]);
|
||||||
|
|
||||||
if (!$options['blocking']) {
|
if (!$options['blocking']) {
|
||||||
fclose($socket);
|
fclose($socket);
|
||||||
$fake_headers = '';
|
$fake_headers = '';
|
||||||
$options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers));
|
$options['hooks']->dispatch('fsockopen.after_request', [&$fake_headers]);
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$timeout_sec = (int) floor($options['timeout']);
|
$timeout_sec = (int) floor($options['timeout']);
|
||||||
if ($timeout_sec === $options['timeout']) {
|
if ($timeout_sec === $options['timeout']) {
|
||||||
$timeout_msec = 0;
|
$timeout_msec = 0;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS;
|
$timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_set_timeout($socket, $timeout_sec, $timeout_msec);
|
stream_set_timeout($socket, $timeout_sec, $timeout_msec);
|
||||||
|
|
||||||
$response = '';
|
$response = '';
|
||||||
|
@ -241,13 +275,18 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
$doingbody = false;
|
$doingbody = false;
|
||||||
$download = false;
|
$download = false;
|
||||||
if ($options['filename']) {
|
if ($options['filename']) {
|
||||||
$download = fopen($options['filename'], 'wb');
|
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
|
||||||
|
$download = @fopen($options['filename'], 'wb');
|
||||||
|
if ($download === false) {
|
||||||
|
$error = error_get_last();
|
||||||
|
throw new Exception($error['message'], 'fopen');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!feof($socket)) {
|
while (!feof($socket)) {
|
||||||
$this->info = stream_get_meta_data($socket);
|
$this->info = stream_get_meta_data($socket);
|
||||||
if ($this->info['timed_out']) {
|
if ($this->info['timed_out']) {
|
||||||
throw new Requests_Exception('fsocket timed out', 'timeout');
|
throw new Exception('fsocket timed out', 'timeout');
|
||||||
}
|
}
|
||||||
|
|
||||||
$block = fread($socket, Requests::BUFFER_SIZE);
|
$block = fread($socket, Requests::BUFFER_SIZE);
|
||||||
|
@ -261,13 +300,14 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
|
|
||||||
// Are we in body mode now?
|
// Are we in body mode now?
|
||||||
if ($doingbody) {
|
if ($doingbody) {
|
||||||
$options['hooks']->dispatch('request.progress', array($block, $size, $this->max_bytes));
|
$options['hooks']->dispatch('request.progress', [$block, $size, $this->max_bytes]);
|
||||||
$data_length = strlen($block);
|
$data_length = strlen($block);
|
||||||
if ($this->max_bytes) {
|
if ($this->max_bytes) {
|
||||||
// Have we already hit a limit?
|
// Have we already hit a limit?
|
||||||
if ($size === $this->max_bytes) {
|
if ($size === $this->max_bytes) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($size + $data_length) > $this->max_bytes) {
|
if (($size + $data_length) > $this->max_bytes) {
|
||||||
// Limit the length
|
// Limit the length
|
||||||
$limited_length = ($this->max_bytes - $size);
|
$limited_length = ($this->max_bytes - $size);
|
||||||
|
@ -278,49 +318,64 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
$size += strlen($block);
|
$size += strlen($block);
|
||||||
if ($download) {
|
if ($download) {
|
||||||
fwrite($download, $block);
|
fwrite($download, $block);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$body .= $block;
|
$body .= $block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->headers = $headers;
|
$this->headers = $headers;
|
||||||
|
|
||||||
if ($download) {
|
if ($download) {
|
||||||
fclose($download);
|
fclose($download);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$this->headers .= "\r\n\r\n" . $body;
|
$this->headers .= "\r\n\r\n" . $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose($socket);
|
fclose($socket);
|
||||||
|
|
||||||
$options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers, &$this->info));
|
$options['hooks']->dispatch('fsockopen.after_request', [&$this->headers, &$this->info]);
|
||||||
return $this->headers;
|
return $this->headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send multiple requests simultaneously
|
* Send multiple requests simultaneously
|
||||||
*
|
*
|
||||||
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request}
|
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see \WpOrg\Requests\Transport::request()}
|
||||||
* @param array $options Global options, see {@see Requests::response()} for documentation
|
* @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||||
* @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
|
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||||
*/
|
*/
|
||||||
public function request_multiple($requests, $options) {
|
public function request_multiple($requests, $options) {
|
||||||
$responses = array();
|
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
|
||||||
|
if (empty($requests)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($options) === false) {
|
||||||
|
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
|
||||||
|
}
|
||||||
|
|
||||||
|
$responses = [];
|
||||||
$class = get_class($this);
|
$class = get_class($this);
|
||||||
foreach ($requests as $id => $request) {
|
foreach ($requests as $id => $request) {
|
||||||
try {
|
try {
|
||||||
$handler = new $class();
|
$handler = new $class();
|
||||||
$responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
|
$responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
|
||||||
|
|
||||||
$request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request));
|
$request['options']['hooks']->dispatch('transport.internal.parse_response', [&$responses[$id], $request]);
|
||||||
}
|
} catch (Exception $e) {
|
||||||
catch (Requests_Exception $e) {
|
|
||||||
$responses[$id] = $e;
|
$responses[$id] = $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_string($responses[$id])) {
|
if (!is_string($responses[$id])) {
|
||||||
$request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id));
|
$request['options']['hooks']->dispatch('multiple.request.complete', [&$responses[$id], $id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,8 +387,8 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
*
|
*
|
||||||
* @return string Accept-Encoding header value
|
* @return string Accept-Encoding header value
|
||||||
*/
|
*/
|
||||||
protected static function accept_encoding() {
|
private static function accept_encoding() {
|
||||||
$type = array();
|
$type = [];
|
||||||
if (function_exists('gzinflate')) {
|
if (function_exists('gzinflate')) {
|
||||||
$type[] = 'deflate;q=1.0';
|
$type[] = 'deflate;q=1.0';
|
||||||
}
|
}
|
||||||
|
@ -351,10 +406,10 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
* Format a URL given GET data
|
* Format a URL given GET data
|
||||||
*
|
*
|
||||||
* @param array $url_parts
|
* @param array $url_parts
|
||||||
* @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query}
|
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
|
||||||
* @return string URL with data
|
* @return string URL with data
|
||||||
*/
|
*/
|
||||||
protected static function format_get($url_parts, $data) {
|
private static function format_get($url_parts, $data) {
|
||||||
if (!empty($data)) {
|
if (!empty($data)) {
|
||||||
if (empty($url_parts['query'])) {
|
if (empty($url_parts['query'])) {
|
||||||
$url_parts['query'] = '';
|
$url_parts['query'] = '';
|
||||||
|
@ -363,17 +418,17 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
$url_parts['query'] .= '&' . http_build_query($data, '', '&');
|
$url_parts['query'] .= '&' . http_build_query($data, '', '&');
|
||||||
$url_parts['query'] = trim($url_parts['query'], '&');
|
$url_parts['query'] = trim($url_parts['query'], '&');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($url_parts['path'])) {
|
if (isset($url_parts['path'])) {
|
||||||
if (isset($url_parts['query'])) {
|
if (isset($url_parts['query'])) {
|
||||||
$get = $url_parts['path'] . '?' . $url_parts['query'];
|
$get = $url_parts['path'] . '?' . $url_parts['query'];
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$get = $url_parts['path'];
|
$get = $url_parts['path'];
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$get = '/';
|
$get = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $get;
|
return $get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,13 +456,14 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
* names, leading things like 'https://www.github.com/' to be invalid.
|
* names, leading things like 'https://www.github.com/' to be invalid.
|
||||||
* Instead
|
* Instead
|
||||||
*
|
*
|
||||||
* @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
|
* @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
|
|
||||||
* @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
|
|
||||||
* @param string $host Host name to verify against
|
* @param string $host Host name to verify against
|
||||||
* @param resource $context Stream context
|
* @param resource $context Stream context
|
||||||
* @return bool
|
* @return bool
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
|
||||||
|
* @throws \WpOrg\Requests\Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
|
||||||
*/
|
*/
|
||||||
public function verify_certificate_from_context($host, $context) {
|
public function verify_certificate_from_context($host, $context) {
|
||||||
$meta = stream_context_get_options($context);
|
$meta = stream_context_get_options($context);
|
||||||
|
@ -415,35 +471,33 @@ class Requests_Transport_fsockopen implements Requests_Transport {
|
||||||
// If we don't have SSL options, then we couldn't make the connection at
|
// If we don't have SSL options, then we couldn't make the connection at
|
||||||
// all
|
// all
|
||||||
if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
|
if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
|
||||||
throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error');
|
throw new Exception(rtrim($this->connect_error), 'ssl.connect_error');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
|
$cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
|
||||||
|
|
||||||
return Requests_SSL::verify_certificate($host, $cert);
|
return Ssl::verify_certificate($host, $cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this transport is valid
|
* Self-test whether the transport can be used.
|
||||||
|
*
|
||||||
|
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
|
||||||
*
|
*
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return boolean True if the transport is valid, false otherwise.
|
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
|
||||||
|
* @return bool Whether the transport can be used.
|
||||||
*/
|
*/
|
||||||
public static function test($capabilities = array()) {
|
public static function test($capabilities = []) {
|
||||||
if (!function_exists('fsockopen')) {
|
if (!function_exists('fsockopen')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If needed, check that streams support SSL
|
// If needed, check that streams support SSL
|
||||||
if (isset($capabilities['ssl']) && $capabilities['ssl']) {
|
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
|
||||||
if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
|
if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156
|
|
||||||
if (defined('HHVM_VERSION')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Case-insensitive dictionary, suitable for HTTP headers
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Utility;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use ArrayIterator;
|
||||||
|
use IteratorAggregate;
|
||||||
|
use ReturnTypeWillChange;
|
||||||
|
use WpOrg\Requests\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case-insensitive dictionary, suitable for HTTP headers
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
class CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
|
||||||
|
/**
|
||||||
|
* Actual item data
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $data = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a case insensitive dictionary.
|
||||||
|
*
|
||||||
|
* @param array $data Dictionary/map to convert to case-insensitive
|
||||||
|
*/
|
||||||
|
public function __construct(array $data = []) {
|
||||||
|
foreach ($data as $offset => $value) {
|
||||||
|
$this->offsetSet($offset, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given item exists
|
||||||
|
*
|
||||||
|
* @param string $offset Item key
|
||||||
|
* @return boolean Does the item exist?
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetExists($offset) {
|
||||||
|
if (is_string($offset)) {
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->data[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value for the item
|
||||||
|
*
|
||||||
|
* @param string $offset Item key
|
||||||
|
* @return string|null Item value (null if the item key doesn't exist)
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetGet($offset) {
|
||||||
|
if (is_string($offset)) {
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->data[$offset])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->data[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the given item
|
||||||
|
*
|
||||||
|
* @param string $offset Item name
|
||||||
|
* @param string $value Item value
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetSet($offset, $value) {
|
||||||
|
if ($offset === null) {
|
||||||
|
throw new Exception('Object is a dictionary, not a list', 'invalidset');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($offset)) {
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->data[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset the given header
|
||||||
|
*
|
||||||
|
* @param string $offset
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetUnset($offset) {
|
||||||
|
if (is_string($offset)) {
|
||||||
|
$offset = strtolower($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->data[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator for the data
|
||||||
|
*
|
||||||
|
* @return \ArrayIterator
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function getIterator() {
|
||||||
|
return new ArrayIterator($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the headers as an array
|
||||||
|
*
|
||||||
|
* @return array Header data
|
||||||
|
*/
|
||||||
|
public function getAll() {
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Iterator for arrays requiring filtered values
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Utility;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use ReturnTypeWillChange;
|
||||||
|
use WpOrg\Requests\Exception\InvalidArgument;
|
||||||
|
use WpOrg\Requests\Utility\InputValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator for arrays requiring filtered values
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
final class FilteredIterator extends ArrayIterator {
|
||||||
|
/**
|
||||||
|
* Callback to run as a filter
|
||||||
|
*
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new iterator
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @param callable $callback Callback to be called on each value
|
||||||
|
*
|
||||||
|
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data argument is not iterable.
|
||||||
|
*/
|
||||||
|
public function __construct($data, $callback) {
|
||||||
|
if (InputValidator::is_iterable($data) === false) {
|
||||||
|
throw InvalidArgument::create(1, '$data', 'iterable', gettype($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($data);
|
||||||
|
|
||||||
|
if (is_callable($callback)) {
|
||||||
|
$this->callback = $callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*
|
||||||
|
* @phpcs:disable PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function __unserialize($data) {}
|
||||||
|
// phpcs:enable
|
||||||
|
|
||||||
|
public function __wakeup() {
|
||||||
|
unset($this->callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current item's value after filtering
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function current() {
|
||||||
|
$value = parent::current();
|
||||||
|
|
||||||
|
if (is_callable($this->callback)) {
|
||||||
|
$value = call_user_func($this->callback, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function unserialize($data) {}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Input validation utilities.
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WpOrg\Requests\Utility;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use CurlHandle;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input validation utilities.
|
||||||
|
*
|
||||||
|
* @package Requests\Utilities
|
||||||
|
*/
|
||||||
|
final class InputValidator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that a received input parameter is of type string or is "stringable".
|
||||||
|
*
|
||||||
|
* @param mixed $input Input parameter to verify.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_string_or_stringable($input) {
|
||||||
|
return is_string($input) || self::is_stringable_object($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify whether a received input parameter is usable as an integer array key.
|
||||||
|
*
|
||||||
|
* @param mixed $input Input parameter to verify.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_numeric_array_key($input) {
|
||||||
|
if (is_int($input)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_string($input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) preg_match('`^-?[0-9]+$`', $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify whether a received input parameter is "stringable".
|
||||||
|
*
|
||||||
|
* @param mixed $input Input parameter to verify.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_stringable_object($input) {
|
||||||
|
return is_object($input) && method_exists($input, '__toString');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify whether a received input parameter is _accessible as if it were an array_.
|
||||||
|
*
|
||||||
|
* @param mixed $input Input parameter to verify.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function has_array_access($input) {
|
||||||
|
return is_array($input) || $input instanceof ArrayAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify whether a received input parameter is "iterable".
|
||||||
|
*
|
||||||
|
* @internal The PHP native `is_iterable()` function was only introduced in PHP 7.1
|
||||||
|
* and this library still supports PHP 5.6.
|
||||||
|
*
|
||||||
|
* @param mixed $input Input parameter to verify.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_iterable($input) {
|
||||||
|
return is_array($input) || $input instanceof Traversable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify whether a received input parameter is a Curl handle.
|
||||||
|
*
|
||||||
|
* The PHP Curl extension worked with resources prior to PHP 8.0 and with
|
||||||
|
* an instance of the `CurlHandle` class since PHP 8.0.
|
||||||
|
* {@link https://www.php.net/manual/en/migration80.incompatible.php#migration80.incompatible.resource2object}
|
||||||
|
*
|
||||||
|
* @param mixed $input Input parameter to verify.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_curl_handle($input) {
|
||||||
|
if (is_resource($input)) {
|
||||||
|
return get_resource_type($input) === 'curl';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_object($input)) {
|
||||||
|
return $input instanceof CurlHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -12,10 +12,10 @@
|
||||||
*
|
*
|
||||||
* @since 4.7.0
|
* @since 4.7.0
|
||||||
*
|
*
|
||||||
* @see Requests_Hooks
|
* @see WpOrg\Requests\Hooks
|
||||||
*/
|
*/
|
||||||
#[AllowDynamicProperties]
|
#[AllowDynamicProperties]
|
||||||
class WP_HTTP_Requests_Hooks extends Requests_Hooks {
|
class WP_HTTP_Requests_Hooks extends WpOrg\Requests\Hooks {
|
||||||
/**
|
/**
|
||||||
* Requested URL.
|
* Requested URL.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core wrapper object for a Requests_Response for standardisation.
|
* Core wrapper object for a WpOrg\Requests\Response for standardisation.
|
||||||
*
|
*
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*
|
*
|
||||||
|
@ -19,7 +19,7 @@ class WP_HTTP_Requests_Response extends WP_HTTP_Response {
|
||||||
* Requests Response object.
|
* Requests Response object.
|
||||||
*
|
*
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
* @var Requests_Response
|
* @var \WpOrg\Requests\Response
|
||||||
*/
|
*/
|
||||||
protected $response;
|
protected $response;
|
||||||
|
|
||||||
|
@ -36,10 +36,10 @@ class WP_HTTP_Requests_Response extends WP_HTTP_Response {
|
||||||
*
|
*
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*
|
*
|
||||||
* @param Requests_Response $response HTTP response.
|
* @param \WpOrg\Requests\Response $response HTTP response.
|
||||||
* @param string $filename Optional. File name. Default empty.
|
* @param string $filename Optional. File name. Default empty.
|
||||||
*/
|
*/
|
||||||
public function __construct( Requests_Response $response, $filename = '' ) {
|
public function __construct( WpOrg\Requests\Response $response, $filename = '' ) {
|
||||||
$this->response = $response;
|
$this->response = $response;
|
||||||
$this->filename = $filename;
|
$this->filename = $filename;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class WP_HTTP_Requests_Response extends WP_HTTP_Response {
|
||||||
*
|
*
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*
|
*
|
||||||
* @return Requests_Response HTTP response.
|
* @return WpOrg\Requests\Response HTTP response.
|
||||||
*/
|
*/
|
||||||
public function get_response_object() {
|
public function get_response_object() {
|
||||||
return $this->response;
|
return $this->response;
|
||||||
|
@ -60,11 +60,11 @@ class WP_HTTP_Requests_Response extends WP_HTTP_Response {
|
||||||
*
|
*
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*
|
*
|
||||||
* @return \Requests_Utility_CaseInsensitiveDictionary Map of header name to header value.
|
* @return \WpOrg\Requests\Utility\CaseInsensitiveDictionary Map of header name to header value.
|
||||||
*/
|
*/
|
||||||
public function get_headers() {
|
public function get_headers() {
|
||||||
// Ensure headers remain case-insensitive.
|
// Ensure headers remain case-insensitive.
|
||||||
$converted = new Requests_Utility_CaseInsensitiveDictionary();
|
$converted = new WpOrg\Requests\Utility\CaseInsensitiveDictionary();
|
||||||
|
|
||||||
foreach ( $this->response->headers->getAll() as $key => $value ) {
|
foreach ( $this->response->headers->getAll() as $key => $value ) {
|
||||||
if ( count( $value ) === 1 ) {
|
if ( count( $value ) === 1 ) {
|
||||||
|
@ -85,7 +85,7 @@ class WP_HTTP_Requests_Response extends WP_HTTP_Response {
|
||||||
* @param array $headers Map of header name to header value.
|
* @param array $headers Map of header name to header value.
|
||||||
*/
|
*/
|
||||||
public function set_headers( $headers ) {
|
public function set_headers( $headers ) {
|
||||||
$this->response->headers = new Requests_Response_Headers( $headers );
|
$this->response->headers = new WpOrg\Requests\Response\Headers( $headers );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
* @since 2.7.0
|
* @since 2.7.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ( ! class_exists( 'Requests' ) ) {
|
if ( ! class_exists( 'WpOrg\Requests\Autoload' ) ) {
|
||||||
require ABSPATH . WPINC . '/class-requests.php';
|
require ABSPATH . WPINC . '/Requests/src/Autoload.php';
|
||||||
|
|
||||||
Requests::register_autoloader();
|
WpOrg\Requests\Autoload::register();
|
||||||
Requests::set_certificate_path( ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
|
WpOrg\Requests\Requests::set_certificate_path( ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,14 +275,14 @@ class WP_Http {
|
||||||
if ( empty( $url ) || empty( $parsed_url['scheme'] ) ) {
|
if ( empty( $url ) || empty( $parsed_url['scheme'] ) ) {
|
||||||
$response = new WP_Error( 'http_request_failed', __( 'A valid URL was not provided.' ) );
|
$response = new WP_Error( 'http_request_failed', __( 'A valid URL was not provided.' ) );
|
||||||
/** This action is documented in wp-includes/class-wp-http.php */
|
/** This action is documented in wp-includes/class-wp-http.php */
|
||||||
do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
|
do_action( 'http_api_debug', $response, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url );
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->block_request( $url ) ) {
|
if ( $this->block_request( $url ) ) {
|
||||||
$response = new WP_Error( 'http_request_not_executed', __( 'User has blocked requests through HTTP.' ) );
|
$response = new WP_Error( 'http_request_not_executed', __( 'User has blocked requests through HTTP.' ) );
|
||||||
/** This action is documented in wp-includes/class-wp-http.php */
|
/** This action is documented in wp-includes/class-wp-http.php */
|
||||||
do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
|
do_action( 'http_api_debug', $response, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url );
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ class WP_Http {
|
||||||
if ( ! wp_is_writable( dirname( $parsed_args['filename'] ) ) ) {
|
if ( ! wp_is_writable( dirname( $parsed_args['filename'] ) ) ) {
|
||||||
$response = new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
|
$response = new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
|
||||||
/** This action is documented in wp-includes/class-wp-http.php */
|
/** This action is documented in wp-includes/class-wp-http.php */
|
||||||
do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
|
do_action( 'http_api_debug', $response, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url );
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ class WP_Http {
|
||||||
$options['max_bytes'] = $parsed_args['limit_response_size'];
|
$options['max_bytes'] = $parsed_args['limit_response_size'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've got cookies, use and convert them to Requests_Cookie.
|
// If we've got cookies, use and convert them to WpOrg\Requests\Cookie.
|
||||||
if ( ! empty( $parsed_args['cookies'] ) ) {
|
if ( ! empty( $parsed_args['cookies'] ) ) {
|
||||||
$options['cookies'] = WP_Http::normalize_cookies( $parsed_args['cookies'] );
|
$options['cookies'] = WP_Http::normalize_cookies( $parsed_args['cookies'] );
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ class WP_Http {
|
||||||
// Check for proxies.
|
// Check for proxies.
|
||||||
$proxy = new WP_HTTP_Proxy();
|
$proxy = new WP_HTTP_Proxy();
|
||||||
if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
|
if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
|
||||||
$options['proxy'] = new Requests_Proxy_HTTP( $proxy->host() . ':' . $proxy->port() );
|
$options['proxy'] = new WpOrg\Requests\Proxy\HTTP( $proxy->host() . ':' . $proxy->port() );
|
||||||
|
|
||||||
if ( $proxy->use_authentication() ) {
|
if ( $proxy->use_authentication() ) {
|
||||||
$options['proxy']->use_authentication = true;
|
$options['proxy']->use_authentication = true;
|
||||||
|
@ -392,7 +392,7 @@ class WP_Http {
|
||||||
mbstring_binary_safe_encoding();
|
mbstring_binary_safe_encoding();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$requests_response = Requests::request( $url, $headers, $data, $type, $options );
|
$requests_response = WpOrg\Requests\Requests::request( $url, $headers, $data, $type, $options );
|
||||||
|
|
||||||
// Convert the response into an array.
|
// Convert the response into an array.
|
||||||
$http_response = new WP_HTTP_Requests_Response( $requests_response, $parsed_args['filename'] );
|
$http_response = new WP_HTTP_Requests_Response( $requests_response, $parsed_args['filename'] );
|
||||||
|
@ -400,7 +400,7 @@ class WP_Http {
|
||||||
|
|
||||||
// Add the original object to the array.
|
// Add the original object to the array.
|
||||||
$response['http_response'] = $http_response;
|
$response['http_response'] = $http_response;
|
||||||
} catch ( Requests_Exception $e ) {
|
} catch ( WpOrg\Requests\Exception $e ) {
|
||||||
$response = new WP_Error( 'http_request_failed', $e->getMessage() );
|
$response = new WP_Error( 'http_request_failed', $e->getMessage() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,7 +417,7 @@ class WP_Http {
|
||||||
* @param array $parsed_args HTTP request arguments.
|
* @param array $parsed_args HTTP request arguments.
|
||||||
* @param string $url The request URL.
|
* @param string $url The request URL.
|
||||||
*/
|
*/
|
||||||
do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
|
do_action( 'http_api_debug', $response, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url );
|
||||||
if ( is_wp_error( $response ) ) {
|
if ( is_wp_error( $response ) ) {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
@ -453,10 +453,10 @@ class WP_Http {
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*
|
*
|
||||||
* @param array $cookies Array of cookies to send with the request.
|
* @param array $cookies Array of cookies to send with the request.
|
||||||
* @return Requests_Cookie_Jar Cookie holder object.
|
* @return WpOrg\Requests\Cookie\Jar Cookie holder object.
|
||||||
*/
|
*/
|
||||||
public static function normalize_cookies( $cookies ) {
|
public static function normalize_cookies( $cookies ) {
|
||||||
$cookie_jar = new Requests_Cookie_Jar();
|
$cookie_jar = new WpOrg\Requests\Cookie\Jar();
|
||||||
|
|
||||||
foreach ( $cookies as $name => $value ) {
|
foreach ( $cookies as $name => $value ) {
|
||||||
if ( $value instanceof WP_Http_Cookie ) {
|
if ( $value instanceof WP_Http_Cookie ) {
|
||||||
|
@ -466,9 +466,9 @@ class WP_Http {
|
||||||
return null !== $attr;
|
return null !== $attr;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$cookie_jar[ $value->name ] = new Requests_Cookie( $value->name, $value->value, $attributes, array( 'host-only' => $value->host_only ) );
|
$cookie_jar[ $value->name ] = new WpOrg\Requests\Cookie( $value->name, $value->value, $attributes, array( 'host-only' => $value->host_only ) );
|
||||||
} elseif ( is_scalar( $value ) ) {
|
} elseif ( is_scalar( $value ) ) {
|
||||||
$cookie_jar[ $name ] = new Requests_Cookie( $name, $value );
|
$cookie_jar[ $name ] = new WpOrg\Requests\Cookie( $name, (string) $value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,16 +484,16 @@ class WP_Http {
|
||||||
*
|
*
|
||||||
* @since 4.6.0
|
* @since 4.6.0
|
||||||
*
|
*
|
||||||
* @param string $location URL to redirect to.
|
* @param string $location URL to redirect to.
|
||||||
* @param array $headers Headers for the redirect.
|
* @param array $headers Headers for the redirect.
|
||||||
* @param string|array $data Body to send with the request.
|
* @param string|array $data Body to send with the request.
|
||||||
* @param array $options Redirect request options.
|
* @param array $options Redirect request options.
|
||||||
* @param Requests_Response $original Response object.
|
* @param WpOrg\Requests\Response $original Response object.
|
||||||
*/
|
*/
|
||||||
public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
|
public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
|
||||||
// Browser compatibility.
|
// Browser compatibility.
|
||||||
if ( 302 === $original->status_code ) {
|
if ( 302 === $original->status_code ) {
|
||||||
$options['type'] = Requests::GET;
|
$options['type'] = WpOrg\Requests\Requests::GET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,12 +502,12 @@ class WP_Http {
|
||||||
*
|
*
|
||||||
* @since 4.7.5
|
* @since 4.7.5
|
||||||
*
|
*
|
||||||
* @throws Requests_Exception On unsuccessful URL validation.
|
* @throws WpOrg\Requests\Exception On unsuccessful URL validation.
|
||||||
* @param string $location URL to redirect to.
|
* @param string $location URL to redirect to.
|
||||||
*/
|
*/
|
||||||
public static function validate_redirects( $location ) {
|
public static function validate_redirects( $location ) {
|
||||||
if ( ! wp_http_validate_url( $location ) ) {
|
if ( ! wp_http_validate_url( $location ) ) {
|
||||||
throw new Requests_Exception( __( 'A valid URL was not provided.' ), 'wp_http.redirect_failed_validation' );
|
throw new WpOrg\Requests\Exception( __( 'A valid URL was not provided.' ), 'wp_http.redirect_failed_validation' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3654,7 +3654,7 @@ function post_permalink( $post = 0 ) {
|
||||||
* @param string|bool $file_path Optional. File path to write request to. Default false.
|
* @param string|bool $file_path Optional. File path to write request to. Default false.
|
||||||
* @param int $red Optional. The number of Redirects followed, Upon 5 being hit,
|
* @param int $red Optional. The number of Redirects followed, Upon 5 being hit,
|
||||||
* returns false. Default 1.
|
* returns false. Default 1.
|
||||||
* @return \Requests_Utility_CaseInsensitiveDictionary|false Headers on success, false on failure.
|
* @return \WpOrg\Requests\Utility\CaseInsensitiveDictionary|false Headers on success, false on failure.
|
||||||
*/
|
*/
|
||||||
function wp_get_http( $url, $file_path = false, $red = 1 ) {
|
function wp_get_http( $url, $file_path = false, $red = 1 ) {
|
||||||
_deprecated_function( __FUNCTION__, '4.4.0', 'WP_Http' );
|
_deprecated_function( __FUNCTION__, '4.4.0', 'WP_Http' );
|
||||||
|
|
|
@ -976,7 +976,7 @@ function do_enclose( $content, $post ) {
|
||||||
*
|
*
|
||||||
* @param string $url URL to retrieve HTTP headers from.
|
* @param string $url URL to retrieve HTTP headers from.
|
||||||
* @param bool $deprecated Not Used.
|
* @param bool $deprecated Not Used.
|
||||||
* @return \Requests_Utility_CaseInsensitiveDictionary|false Headers on success, false on failure.
|
* @return \WpOrg\Requests\Utility\CaseInsensitiveDictionary|false Headers on success, false on failure.
|
||||||
*/
|
*/
|
||||||
function wp_get_http_headers( $url, $deprecated = false ) {
|
function wp_get_http_headers( $url, $deprecated = false ) {
|
||||||
if ( ! empty( $deprecated ) ) {
|
if ( ! empty( $deprecated ) ) {
|
||||||
|
|
|
@ -200,13 +200,13 @@ function wp_remote_head( $url, $args = array() ) {
|
||||||
* Retrieve only the headers from the raw response.
|
* Retrieve only the headers from the raw response.
|
||||||
*
|
*
|
||||||
* @since 2.7.0
|
* @since 2.7.0
|
||||||
* @since 4.6.0 Return value changed from an array to an Requests_Utility_CaseInsensitiveDictionary instance.
|
* @since 4.6.0 Return value changed from an array to an WpOrg\Requests\Utility\CaseInsensitiveDictionary instance.
|
||||||
*
|
*
|
||||||
* @see \Requests_Utility_CaseInsensitiveDictionary
|
* @see \WpOrg\Requests\Utility\CaseInsensitiveDictionary
|
||||||
*
|
*
|
||||||
* @param array|WP_Error $response HTTP response.
|
* @param array|WP_Error $response HTTP response.
|
||||||
* @return \Requests_Utility_CaseInsensitiveDictionary|array The headers of the response, or empty array
|
* @return \WpOrg\Requests\Utility\CaseInsensitiveDictionary|array The headers of the response, or empty array
|
||||||
* if incorrect parameter given.
|
* if incorrect parameter given.
|
||||||
*/
|
*/
|
||||||
function wp_remote_retrieve_headers( $response ) {
|
function wp_remote_retrieve_headers( $response ) {
|
||||||
if ( is_wp_error( $response ) || ! isset( $response['headers'] ) ) {
|
if ( is_wp_error( $response ) || ! isset( $response['headers'] ) ) {
|
||||||
|
|
|
@ -1414,7 +1414,7 @@ function rest_parse_request_arg( $value, $request, $param ) {
|
||||||
function rest_is_ip_address( $ip ) {
|
function rest_is_ip_address( $ip ) {
|
||||||
$ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
|
$ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
|
||||||
|
|
||||||
if ( ! preg_match( $ipv4_pattern, $ip ) && ! Requests_IPv6::check_ipv6( $ip ) ) {
|
if ( ! preg_match( $ipv4_pattern, $ip ) && ! WpOrg\Requests\Ipv6::check_ipv6( $ip ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*
|
*
|
||||||
* @global string $wp_version
|
* @global string $wp_version
|
||||||
*/
|
*/
|
||||||
$wp_version = '6.2-alpha-54996';
|
$wp_version = '6.2-alpha-54997';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||||
|
|
Loading…
Reference in New Issue