Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/cakephp/cakephp/src/Http/Client.php
<?php
/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link          https://cakephp.org CakePHP(tm) Project
 * @since         3.0.0
 * @license       https://opensource.org/licenses/mit-license.php MIT License
 */
namespace Cake\Http;

use
Cake\Core\App;
use
Cake\Core\Exception\Exception;
use
Cake\Core\InstanceConfigTrait;
use
Cake\Http\Client\AdapterInterface;
use
Cake\Http\Client\Adapter\Curl;
use
Cake\Http\Client\Adapter\Stream;
use
Cake\Http\Client\Request;
use
Cake\Http\Cookie\CookieCollection;
use
Cake\Http\Cookie\CookieInterface;
use
Cake\Utility\Hash;
use
InvalidArgumentException;
use
Zend\Diactoros\Uri;

/**
 * The end user interface for doing HTTP requests.
 *
 * ### Scoped clients
 *
 * If you're doing multiple requests to the same hostname it's often convenient
 * to use the constructor arguments to create a scoped client. This allows you
 * to keep your code DRY and not repeat hostnames, authentication, and other options.
 *
 * ### Doing requests
 *
 * Once you've created an instance of Client you can do requests
 * using several methods. Each corresponds to a different HTTP method.
 *
 * - get()
 * - post()
 * - put()
 * - delete()
 * - patch()
 *
 * ### Cookie management
 *
 * Client will maintain cookies from the responses done with
 * a client instance. These cookies will be automatically added
 * to future requests to matching hosts. Cookies will respect the
 * `Expires`, `Path` and `Domain` attributes. You can get the client's
 * CookieCollection using cookies()
 *
 * You can use the 'cookieJar' constructor option to provide a custom
 * cookie jar instance you've restored from cache/disk. By default
 * an empty instance of Cake\Http\Client\CookieCollection will be created.
 *
 * ### Sending request bodies
 *
 * By default any POST/PUT/PATCH/DELETE request with $data will
 * send their data as `application/x-www-form-urlencoded` unless
 * there are attached files. In that case `multipart/form-data`
 * will be used.
 *
 * When sending request bodies you can use the `type` option to
 * set the Content-Type for the request:
 *
 * ```
 * $http->get('/users', [], ['type' => 'json']);
 * ```
 *
 * The `type` option sets both the `Content-Type` and `Accept` header, to
 * the same mime type. When using `type` you can use either a full mime
 * type or an alias. If you need different types in the Accept and Content-Type
 * headers you should set them manually and not use `type`
 *
 * ### Using authentication
 *
 * By using the `auth` key you can use authentication. The type sub option
 * can be used to specify which authentication strategy you want to use.
 * CakePHP comes with a few built-in strategies:
 *
 * - Basic
 * - Digest
 * - Oauth
 *
 * ### Using proxies
 *
 * By using the `proxy` key you can set authentication credentials for
 * a proxy if you need to use one. The type sub option can be used to
 * specify which authentication strategy you want to use.
 * CakePHP comes with built-in support for basic authentication.
 */
class Client
{
    use
InstanceConfigTrait;

   
/**
     * Default configuration for the client.
     *
     * @var array
     */
   
protected $_defaultConfig = [
       
'adapter' => null,
       
'host' => null,
       
'port' => null,
       
'scheme' => 'http',
       
'timeout' => 30,
       
'ssl_verify_peer' => true,
       
'ssl_verify_peer_name' => true,
       
'ssl_verify_depth' => 5,
       
'ssl_verify_host' => true,
       
'redirect' => false,
       
'protocolVersion' => '1.1',
    ];

   
/**
     * List of cookies from responses made with this client.
     *
     * Cookies are indexed by the cookie's domain or
     * request host name.
     *
     * @var \Cake\Http\Cookie\CookieCollection
     */
   
protected $_cookies;

   
/**
     * Adapter for sending requests.
     *
     * @var \Cake\Http\Client\AdapterInterface
     */
   
protected $_adapter;

   
/**
     * Create a new HTTP Client.
     *
     * ### Config options
     *
     * You can set the following options when creating a client:
     *
     * - host - The hostname to do requests on.
     * - port - The port to use.
     * - scheme - The default scheme/protocol to use. Defaults to http.
     * - timeout - The timeout in seconds. Defaults to 30
     * - ssl_verify_peer - Whether or not SSL certificates should be validated.
     *   Defaults to true.
     * - ssl_verify_peer_name - Whether or not peer names should be validated.
     *   Defaults to true.
     * - ssl_verify_depth - The maximum certificate chain depth to traverse.
     *   Defaults to 5.
     * - ssl_verify_host - Verify that the certificate and hostname match.
     *   Defaults to true.
     * - redirect - Number of redirects to follow. Defaults to false.
     * - adapter - The adapter class name or instance. Defaults to
     *   \Cake\Http\Client\Adapter\Curl if `curl` extension is loaded else
     *   \Cake\Http\Client\Adapter\Stream.
     * - protocolVersion - The HTTP protocol version to use. Defaults to 1.1
     *
     * @param array $config Config options for scoped clients.
     * @throws \InvalidArgumentException
     */
   
public function __construct($config = [])
    {
       
$this->setConfig($config);

       
$adapter = $this->_config['adapter'];
        if (
$adapter === null) {
           
$adapter = Curl::class;

            if (!
extension_loaded('curl')) {
               
$adapter = Stream::class;
            }
        } else {
           
$this->setConfig('adapter', null);
        }

        if (
is_string($adapter)) {
           
$adapter = new $adapter();
        }

        if (!
$adapter instanceof AdapterInterface) {
            throw new
InvalidArgumentException('Adapter must be an instance of Cake\Http\Client\AdapterInterface');
        }
       
$this->_adapter = $adapter;

        if (!empty(
$this->_config['cookieJar'])) {
           
$this->_cookies = $this->_config['cookieJar'];
           
$this->setConfig('cookieJar', null);
        } else {
           
$this->_cookies = new CookieCollection();
        }
    }

   
/**
     * Get the cookies stored in the Client.
     *
     * @return \Cake\Http\Cookie\CookieCollection
     */
   
public function cookies()
    {
        return
$this->_cookies;
    }

   
/**
     * Adds a cookie to the Client collection.
     *
     * @param \Cake\Http\Cookie\CookieInterface $cookie Cookie object.
     * @return $this
     * @throws \InvalidArgumentException
     */
   
public function addCookie(CookieInterface $cookie)
    {
        if (!
$cookie->getDomain() || !$cookie->getPath()) {
            throw new
InvalidArgumentException('Cookie must have a domain and a path set.');
        }
       
$this->_cookies = $this->_cookies->add($cookie);

        return
$this;
    }

   
/**
     * Do a GET request.
     *
     * The $data argument supports a special `_content` key
     * for providing a request body in a GET request. This is
     * generally not used, but services like ElasticSearch use
     * this feature.
     *
     * @param string $url The url or path you want to request.
     * @param array $data The query data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function get($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$body = null;
        if (isset(
$data['_content'])) {
           
$body = $data['_content'];
            unset(
$data['_content']);
        }
       
$url = $this->buildUrl($url, $data, $options);

        return
$this->_doRequest(
           
Request::METHOD_GET,
           
$url,
           
$body,
           
$options
       
);
    }

   
/**
     * Do a POST request.
     *
     * @param string $url The url or path you want to request.
     * @param mixed $data The post data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function post($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, [], $options);

        return
$this->_doRequest(Request::METHOD_POST, $url, $data, $options);
    }

   
/**
     * Do a PUT request.
     *
     * @param string $url The url or path you want to request.
     * @param mixed $data The request data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function put($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, [], $options);

        return
$this->_doRequest(Request::METHOD_PUT, $url, $data, $options);
    }

   
/**
     * Do a PATCH request.
     *
     * @param string $url The url or path you want to request.
     * @param mixed $data The request data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function patch($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, [], $options);

        return
$this->_doRequest(Request::METHOD_PATCH, $url, $data, $options);
    }

   
/**
     * Do an OPTIONS request.
     *
     * @param string $url The url or path you want to request.
     * @param mixed $data The request data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function options($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, [], $options);

        return
$this->_doRequest(Request::METHOD_OPTIONS, $url, $data, $options);
    }

   
/**
     * Do a TRACE request.
     *
     * @param string $url The url or path you want to request.
     * @param mixed $data The request data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function trace($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, [], $options);

        return
$this->_doRequest(Request::METHOD_TRACE, $url, $data, $options);
    }

   
/**
     * Do a DELETE request.
     *
     * @param string $url The url or path you want to request.
     * @param mixed $data The request data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function delete($url, $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, [], $options);

        return
$this->_doRequest(Request::METHOD_DELETE, $url, $data, $options);
    }

   
/**
     * Do a HEAD request.
     *
     * @param string $url The url or path you want to request.
     * @param array $data The query string data you want to send.
     * @param array $options Additional options for the request.
     * @return \Cake\Http\Client\Response
     */
   
public function head($url, array $data = [], array $options = [])
    {
       
$options = $this->_mergeOptions($options);
       
$url = $this->buildUrl($url, $data, $options);

        return
$this->_doRequest(Request::METHOD_HEAD, $url, '', $options);
    }

   
/**
     * Helper method for doing non-GET requests.
     *
     * @param string $method HTTP method.
     * @param string $url URL to request.
     * @param mixed $data The request body.
     * @param array $options The options to use. Contains auth, proxy, etc.
     * @return \Cake\Http\Client\Response
     */
   
protected function _doRequest($method, $url, $data, $options)
    {
       
$request = $this->_createRequest(
           
$method,
           
$url,
           
$data,
           
$options
       
);

        return
$this->send($request, $options);
    }

   
/**
     * Does a recursive merge of the parameter with the scope config.
     *
     * @param array $options Options to merge.
     * @return array Options merged with set config.
     */
   
protected function _mergeOptions($options)
    {
        return
Hash::merge($this->_config, $options);
    }

   
/**
     * Send a request.
     *
     * Used internally by other methods, but can also be used to send
     * handcrafted Request objects.
     *
     * @param \Cake\Http\Client\Request $request The request to send.
     * @param array $options Additional options to use.
     * @return \Cake\Http\Client\Response
     */
   
public function send(Request $request, $options = [])
    {
       
$redirects = 0;
        if (isset(
$options['redirect'])) {
           
$redirects = (int)$options['redirect'];
            unset(
$options['redirect']);
        }

        do {
           
$response = $this->_sendRequest($request, $options);

           
$handleRedirect = $response->isRedirect() && $redirects-- > 0;
            if (
$handleRedirect) {
               
$url = $request->getUri();

               
$location = $response->getHeaderLine('Location');
               
$locationUrl = $this->buildUrl($location, [], [
                   
'host' => $url->getHost(),
                   
'port' => $url->getPort(),
                   
'scheme' => $url->getScheme(),
                   
'protocolRelative' => true,
                ]);
               
$request = $request->withUri(new Uri($locationUrl));
               
$request = $this->_cookies->addToRequest($request, []);
            }
        } while (
$handleRedirect);

        return
$response;
    }

   
/**
     * Send a request without redirection.
     *
     * @param \Cake\Http\Client\Request $request The request to send.
     * @param array $options Additional options to use.
     * @return \Cake\Http\Client\Response
     */
   
protected function _sendRequest(Request $request, $options)
    {
       
$responses = $this->_adapter->send($request, $options);
       
$url = $request->getUri();
        foreach (
$responses as $response) {
           
$this->_cookies = $this->_cookies->addFromResponse($response, $request);
        }

        return
array_pop($responses);
    }

   
/**
     * Generate a URL based on the scoped client options.
     *
     * @param string $url Either a full URL or just the path.
     * @param string|array $query The query data for the URL.
     * @param array $options The config options stored with Client::config()
     * @return string A complete url with scheme, port, host, and path.
     */
   
public function buildUrl($url, $query = [], $options = [])
    {
        if (empty(
$options) && empty($query)) {
            return
$url;
        }
        if (
$query) {
           
$q = (strpos($url, '?') === false) ? '?' : '&';
           
$url .= $q;
           
$url .= is_string($query) ? $query : http_build_query($query);
        }
       
$defaults = [
           
'host' => null,
           
'port' => null,
           
'scheme' => 'http',
           
'protocolRelative' => false,
        ];
       
$options += $defaults;

        if (
$options['protocolRelative'] && preg_match('#^//#', $url)) {
           
$url = $options['scheme'] . ':' . $url;
        }
        if (
preg_match('#^https?://#', $url)) {
            return
$url;
        }

       
$defaultPorts = [
           
'http' => 80,
           
'https' => 443,
        ];
       
$out = $options['scheme'] . '://' . $options['host'];
        if (
$options['port'] && $options['port'] != $defaultPorts[$options['scheme']]) {
           
$out .= ':' . $options['port'];
        }
       
$out .= '/' . ltrim($url, '/');

        return
$out;
    }

   
/**
     * Creates a new request object based on the parameters.
     *
     * @param string $method HTTP method name.
     * @param string $url The url including query string.
     * @param mixed $data The request body.
     * @param array $options The options to use. Contains auth, proxy, etc.
     * @return \Cake\Http\Client\Request
     */
   
protected function _createRequest($method, $url, $data, $options)
    {
       
$headers = isset($options['headers']) ? (array)$options['headers'] : [];
        if (isset(
$options['type'])) {
           
$headers = array_merge($headers, $this->_typeHeaders($options['type']));
        }
        if (
is_string($data) && !isset($headers['Content-Type']) && !isset($headers['content-type'])) {
           
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
        }

       
$request = new Request($url, $method, $headers, $data);
       
$request = $request->withProtocolVersion($this->getConfig('protocolVersion'));

       
$cookies = isset($options['cookies']) ? $options['cookies'] : [];
       
/** @var \Cake\Http\Client\Request $request */
       
$request = $this->_cookies->addToRequest($request, $cookies);
        if (isset(
$options['auth'])) {
           
$request = $this->_addAuthentication($request, $options);
        }
        if (isset(
$options['proxy'])) {
           
$request = $this->_addProxy($request, $options);
        }

        return
$request;
    }

   
/**
     * Returns headers for Accept/Content-Type based on a short type
     * or full mime-type.
     *
     * @param string $type short type alias or full mimetype.
     * @return string[] Headers to set on the request.
     * @throws \Cake\Core\Exception\Exception When an unknown type alias is used.
     */
   
protected function _typeHeaders($type)
    {
        if (
strpos($type, '/') !== false) {
            return [
               
'Accept' => $type,
               
'Content-Type' => $type,
            ];
        }
       
$typeMap = [
           
'json' => 'application/json',
           
'xml' => 'application/xml',
        ];
        if (!isset(
$typeMap[$type])) {
            throw new
Exception("Unknown type alias '$type'.");
        }

        return [
           
'Accept' => $typeMap[$type],
           
'Content-Type' => $typeMap[$type],
        ];
    }

   
/**
     * Add authentication headers to the request.
     *
     * Uses the authentication type to choose the correct strategy
     * and use its methods to add headers.
     *
     * @param \Cake\Http\Client\Request $request The request to modify.
     * @param array $options Array of options containing the 'auth' key.
     * @return \Cake\Http\Client\Request The updated request object.
     */
   
protected function _addAuthentication(Request $request, $options)
    {
       
$auth = $options['auth'];
       
/** @var \Cake\Http\Client\Auth\Basic $adapter */
       
$adapter = $this->_createAuth($auth, $options);
       
$result = $adapter->authentication($request, $options['auth']);

        return
$result ?: $request;
    }

   
/**
     * Add proxy authentication headers.
     *
     * Uses the authentication type to choose the correct strategy
     * and use its methods to add headers.
     *
     * @param \Cake\Http\Client\Request $request The request to modify.
     * @param array $options Array of options containing the 'proxy' key.
     * @return \Cake\Http\Client\Request The updated request object.
     */
   
protected function _addProxy(Request $request, $options)
    {
       
$auth = $options['proxy'];
       
/** @var \Cake\Http\Client\Auth\Basic $adapter */
       
$adapter = $this->_createAuth($auth, $options);
       
$result = $adapter->proxyAuthentication($request, $options['proxy']);

        return
$result ?: $request;
    }

   
/**
     * Create the authentication strategy.
     *
     * Use the configuration options to create the correct
     * authentication strategy handler.
     *
     * @param array $auth The authentication options to use.
     * @param array $options The overall request options to use.
     * @return object Authentication strategy instance.
     * @throws \Cake\Core\Exception\Exception when an invalid strategy is chosen.
     */
   
protected function _createAuth($auth, $options)
    {
        if (empty(
$auth['type'])) {
           
$auth['type'] = 'basic';
        }
       
$name = ucfirst($auth['type']);
       
$class = App::className($name, 'Http/Client/Auth');
        if (!
$class) {
            throw new
Exception(
               
sprintf('Invalid authentication type %s', $name)
            );
        }

        return new
$class($this, $options);
    }
}
// @deprecated 3.4.0 Backwards compatibility with earler 3.x versions.
class_alias('Cake\Http\Client', 'Cake\Network\Http\Client');