Вход Регистрация
Файл: vendor/guzzlehttp/guzzle/src/Client.php
Строк: 1920
<?php

namespace GuzzleHttp;

use 
GuzzleHttpCookieCookieJar;
use 
GuzzleHttpExceptionGuzzleException;
use 
GuzzleHttpExceptionInvalidArgumentException;
use 
GuzzleHttpHandlerCurlShareHandleState;
use 
GuzzleHttpPromise as P;
use 
GuzzleHttpPromisePromiseInterface;
use 
PsrHttpMessageRequestInterface;
use 
PsrHttpMessageResponseInterface;
use 
PsrHttpMessageStreamInterface;
use 
PsrHttpMessageUriInterface;

/**
 * @final
 */
class Client implements ClientInterfacePsrHttpClientClientInterface
{
    use 
ClientTrait;

    
/**
     * @var array Default request options
     */
    
private $config;

    
/**
     * Clients accept an array of constructor parameters.
     *
     * Here's an example of creating a client using a base_uri and an array of
     * default request options to apply to each request:
     *
     *     $client = new Client([
     *         'base_uri'        => 'http://www.foo.com/1.0/',
     *         'timeout'         => 0,
     *         'allow_redirects' => false,
     *         'proxy'           => '192.168.16.1:10'
     *     ]);
     *
     * Client configuration settings include the following options:
     *
     * - handler: (callable) Function that transfers HTTP requests over the
     *   wire. The function is called with a Psr7HttpMessageRequestInterface
     *   and array of transfer options, and must return a
     *   GuzzleHttpPromisePromiseInterface that is fulfilled with a
     *   Psr7HttpMessageResponseInterface on success.
     *   If no handler is provided, a default handler will be created
     *   that enables all of the request options below by attaching all of the
     *   default middleware to the handler.
     * - base_uri: (string|UriInterface) Base URI of the client that is merged
     *   into relative URIs. Can be a string or instance of UriInterface.
     * - transport_sharing: (string|null) Transport sharing mode for the
     *   default handler. Accepts TransportSharing::* or null. Defaults to null.
     * - **: any request option
     *
     * @param array $config Client configuration settings.
     *
     * @see RequestOptions for a list of available request options.
     */
    
public function __construct(array $config = [])
    {
        
$transportSharing array_key_exists('transport_sharing'$config) ? $config['transport_sharing'] : null;
        
$transportSharingMode CurlShareHandleState::normalizeMode($transportSharing'transport_sharing');
        unset(
$config['transport_sharing']);

        if (!isset(
$config['handler'])) {
            
$config['handler'] = $transportSharingMode === TransportSharing::NONE
                
HandlerStack::create()
                : 
HandlerStack::create(Utils::chooseHandler(['transport_sharing' => $transportSharingMode]));
        } elseif (!
is_callable($config['handler'])) {
            throw new 
InvalidArgumentException('handler must be a callable');
        } elseif (
$transportSharingMode === TransportSharing::HANDLER_REQUIRE) {
            throw new 
InvalidArgumentException('The "transport_sharing" client option can only require sharing when Guzzle creates the default handler. Configure the "transport_sharing" option on CurlHandler or CurlMultiHandler when providing a custom cURL handler.');
        }

        
// Convert the base_uri to a UriInterface
        
if (isset($config['base_uri'])) {
            
$config['base_uri'] = Psr7Utils::uriFor($config['base_uri']);
        }

        
$this->configureDefaults($config);
    }

    
/**
     * @param string $method
     * @param array  $args
     *
     * @return PromiseInterface|ResponseInterface
     *
     * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0.
     */
    
public function __call($method$args)
    {
        if (
count($args) < 1) {
            throw new 
InvalidArgumentException('Magic request methods require a URI and optional options array');
        }

        
$uri $args[0];
        
$opts $args[1] ?? [];

        
$isAsync substr($method, -5) === 'Async';
        
$method $isAsync substr($method0, -5) : $method;
        
$method strtoupper($method);

        return 
$isAsync
            
$this->requestAsync($method$uri$opts)
            : 
$this->request($method$uri$opts);
    }

    
/**
     * Asynchronously send an HTTP request.
     *
     * @param array $options Request options to apply to the given
     *                       request and to the transfer. See {@see RequestOptions}.
     */
    
public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface
    
{
        
// Merge the base URI into the request URI if needed.
        
$options $this->prepareDefaults($options);

        return 
$this->transfer(
            
$request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
            
$options
        
);
    }

    
/**
     * Send an HTTP request.
     *
     * @param array $options Request options to apply to the given
     *                       request and to the transfer. See {@see RequestOptions}.
     *
     * @throws GuzzleException
     */
    
public function send(RequestInterface $request, array $options = []): ResponseInterface
    
{
        
$options[RequestOptions::SYNCHRONOUS] = true;

        return 
$this->sendAsync($request$options)->wait();
    }

    
/**
     * The HttpClient PSR (PSR-18) specify this method.
     *
     * {@inheritDoc}
     */
    
public function sendRequest(RequestInterface $request): ResponseInterface
    
{
        
$options[RequestOptions::SYNCHRONOUS] = true;
        
$options[RequestOptions::ALLOW_REDIRECTS] = false;
        
$options[RequestOptions::HTTP_ERRORS] = false;

        return 
$this->sendAsync($request$options)->wait();
    }

    
/**
     * Create and send an asynchronous HTTP request.
     *
     * Use an absolute path to override the base path of the client, or a
     * relative path to append to the base path of the client. The URL can
     * contain the query string as well. Use an array to provide a URL
     * template and additional variables to use in the URL template expansion.
     *
     * @param string              $method  HTTP method
     * @param string|UriInterface $uri     URI object or string.
     * @param array               $options Request options to apply. See {@see RequestOptions}.
     */
    
public function requestAsync(string $method$uri '', array $options = []): PromiseInterface
    
{
        
$normalizedMethod strtoupper($method);
        if (
$method !== $normalizedMethod) {
            
trigger_deprecation(
                
'guzzlehttp/guzzle',
                
'7.11',
                
'Passing a non-uppercase HTTP method to Client::requestAsync() is deprecated; guzzlehttp/guzzle 8.0 will preserve HTTP method casing. Pass an uppercase method explicitly if uppercase is required.'
            
);
            
$method $normalizedMethod;
        }

        
$options $this->prepareDefaults($options);
        
// Remove request modifying parameter because it can be done up-front.
        
$headers $options['headers'] ?? [];
        
$droppedHeaderNames self::castDeprecatedHeaderOptionValues($headers);
        if (
$droppedHeaderNames !== [] && isset($options['_conditional'])) {
            
$options['_conditional'] = Psr7Utils::caselessRemove($droppedHeaderNames$options['_conditional']);
        }
        
$body $options['body'] ?? null;
        
$version self::normalizeProtocolVersion($options['version'] ?? '1.1');
        
// Merge the URI into the base URI.
        
$uri $this->buildUri(Psr7Utils::uriFor($uri), $options);
        if (
is_array($body)) {
            throw 
$this->invalidBody();
        }
        
$body self::createBodyStream($body);
        
$request = new Psr7Request($method$uri$headers$body$version);
        
// Remove the option so that they are not doubly-applied.
        
unset($options['headers'], $options['body'], $options['version']);

        return 
$this->transfer($request$options);
    }

    
/**
     * Create and send an HTTP request.
     *
     * Use an absolute path to override the base path of the client, or a
     * relative path to append to the base path of the client. The URL can
     * contain the query string as well.
     *
     * @param string              $method  HTTP method.
     * @param string|UriInterface $uri     URI object or string.
     * @param array               $options Request options to apply. See {@see RequestOptions}.
     *
     * @throws GuzzleException
     */
    
public function request(string $method$uri '', array $options = []): ResponseInterface
    
{
        
$normalizedMethod strtoupper($method);
        if (
$method !== $normalizedMethod) {
            
trigger_deprecation(
                
'guzzlehttp/guzzle',
                
'7.11',
                
'Passing a non-uppercase HTTP method to Client::request() is deprecated; guzzlehttp/guzzle 8.0 will preserve HTTP method casing. Pass an uppercase method explicitly if uppercase is required.'
            
);
            
$method $normalizedMethod;
        }

        
$options[RequestOptions::SYNCHRONOUS] = true;

        return 
$this->requestAsync($method$uri$options)->wait();
    }

    
/**
     * Get a client configuration option.
     *
     * These options include default request options of the client, a "handler"
     * (if utilized by the concrete client), and a "base_uri" if utilized by
     * the concrete client.
     *
     * @param string|null $option The config option to retrieve.
     *
     * @return mixed
     */
    
public function getConfig(?string $option null)
    {
        return 
$option === null
            
$this->config
            
: ($this->config[$option] ?? null);
    }

    private function 
buildUri(UriInterface $uri, array $config): UriInterface
    
{
        if (isset(
$config['base_uri'])) {
            
$uri Psr7UriResolver::resolve(Psr7Utils::uriFor($config['base_uri']), $uri);
        }

        
$idnOptions Utils::normalizeIdnConversionOption($config['idn_conversion'] ?? null);
        if (
$idnOptions !== null) {
            
$uri Utils::idnUriConvert($uri$idnOptions);
        }

        return 
$uri->getScheme() === '' && $uri->getHost() !== '' $uri->withScheme('http') : $uri;
    }

    
/**
     * Configures the default options for a client.
     */
    
private function configureDefaults(array $config): void
    
{
        
$defaults = [
            
'allow_redirects' => RedirectMiddleware::$defaultSettings,
            
'http_errors' => true,
            
'decode_content' => true,
            
'verify' => true,
            
'cookies' => false,
            
'idn_conversion' => false,
            
'protocols' => ['http''https'],
        ];

        
// Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.

        // We can only trust the HTTP_PROXY environment variable in a CLI
        // process due to the fact that PHP has no reliable mechanism to
        // get environment variables that start with "HTTP_".
        
if (PHP_SAPI === 'cli' && ($proxy Utils::getenv('HTTP_PROXY'))) {
            
$defaults['proxy']['http'] = $proxy;
        }

        if (
$proxy Utils::getenv('HTTPS_PROXY')) {
            
$defaults['proxy']['https'] = $proxy;
        }

        if (
$noProxy Utils::getenv('NO_PROXY')) {
            
$cleanedNoProxy str_replace(' '''$noProxy);
            
$defaults['proxy']['no'] = explode(','$cleanedNoProxy);
        }

        
$this->config $config $defaults;

        if (!empty(
$config['cookies']) && $config['cookies'] === true) {
            
$this->config['cookies'] = new CookieJar();
        }

        
// Add the default user-agent header.
        
if (!isset($this->config['headers'])) {
            
$this->config['headers'] = ['User-Agent' => Utils::defaultUserAgent()];
        } else {
            
// Add the User-Agent header if one was not already set.
            
$hasUserAgent false;
            foreach (
array_keys($this->config['headers']) as $name) {
                if (
strtolower((string) $name) === 'user-agent') {
                    
$hasUserAgent true;
                    break;
                }
            }

            if (!
$hasUserAgent) {
                
$this->config['headers']['User-Agent'] = Utils::defaultUserAgent();
            }
        }

        if (
is_array($this->config['headers'])) {
            
self::warnAboutInvalidHeaderOptionTypes($this->config['headers']);
            
self::castDeprecatedHeaderOptionValues($this->config['headers']);
        }
    }

    
/**
     * Merges default options into the array.
     *
     * @param array $options Options to modify by reference
     */
    
private function prepareDefaults(array $options): array
    {
        
self::warnAboutRequestLevelHandler($options);

        
$defaults $this->config;

        if (!empty(
$defaults['headers'])) {
            
// Default headers are only added if they are not present.
            
$defaults['_conditional'] = $defaults['headers'];
            unset(
$defaults['headers']);
        }

        
// Special handling for headers is required as they are added as
        // conditional headers and as headers passed to a request ctor.
        
if (array_key_exists('headers'$options)) {
            
// Allows default headers to be unset.
            
if ($options['headers'] === null) {
                
$defaults['_conditional'] = [];
                unset(
$options['headers']);
            } elseif (!
is_array($options['headers'])) {
                throw new 
InvalidArgumentException('headers must be an array');
            }
        }

        
// Shallow merge defaults underneath options.
        
$result $options $defaults;

        
// Remove null values.
        
foreach ($result as $k => $v) {
            if (
$v === null) {
                unset(
$result[$k]);
            }
        }

        
self::warnAboutInvalidRequestOptionTypes($result);

        return 
$result;
    }

    private static function 
warnAboutRequestLevelHandler(array $options): void
    
{
        if (!
array_key_exists('handler'$options)) {
            return;
        }

        
trigger_deprecation(
            
'guzzlehttp/guzzle',
            
'7.12',
            
'Passing the "handler" request option is deprecated; guzzlehttp/guzzle 8.0 will ignore request-level handlers. Configure the handler when creating the Client, or use a separate Client instance for requests that need a different handler.'
        
);
    }

    private static function 
warnAboutInvalidRequestOptionTypes(array $options): void
    
{
        if (isset(
$options['handler']) && !is_callable($options['handler'])) {
            
self::warnInvalidRequestOptionType('handler''callable'$options['handler']);
        }

        if (isset(
$options['allow_redirects']) && is_array($options['allow_redirects'])) {
            
self::warnAboutInvalidAllowRedirectsOptionTypes($options['allow_redirects']);
        }

        if (isset(
$options['auth'])) {
            
self::warnAboutInvalidAuthOptionTypes($options['auth']);
        }

        if (isset(
$options['body']) && is_array($options['body'])) {
            
self::warnInvalidRequestOptionType('body''resource|string|null|int|float|bool|StreamInterface|(callable&object)|Iterator|Stringable'$options['body']);
        }

        
self::warnAboutInvalidTlsFileOptionTypes($options'cert');
        
self::warnIfPresentAndNotString($options'cert_type');
        
self::warnIfPresentAndNotNumber($options'connect_timeout');
        
self::warnIfPresentAndNotInt($options'crypto_method');
        
self::warnIfPresentAndNotBoolOrResource($options'debug');
        
self::warnIfPresentAndNotBoolOrString($options'decode_content');
        
self::warnIfPresentAndNotNumber($options'delay');
        
self::warnIfPresentAndNotBoolOrInt($options'expect');

        if (isset(
$options['form_params'])) {
            
self::warnAboutInvalidFormParamTypes($options['form_params']);
        }

        if (isset(
$options['force_ip_resolve']) && !is_string($options['force_ip_resolve'])) {
            
self::warnInvalidRequestOptionType('force_ip_resolve''string'$options['force_ip_resolve']);
        }

        if (isset(
$options['headers'])) {
            
self::warnAboutInvalidHeaderOptionTypes($options['headers']);
        }

        
self::warnIfPresentAndNotBool($options'http_errors');

        if (isset(
$options['multipart'])) {
            
self::warnAboutInvalidMultipartOptionTypes($options['multipart']);
        }

        
self::warnIfPresentAndNotCallable($options'on_headers');
        
self::warnIfPresentAndNotCallable($options'on_stats');
        
self::warnIfPresentAndNotCallable($options'progress');
        
self::warnIfPresentAndNotStringArray($options'protocols'true);
        
self::warnAboutInvalidProxyOptionTypes($options);

        
self::warnIfPresentAndNotNumber($options'read_timeout');
        
self::warnIfPresentAndNotInt($options'retries');

        if (isset(
$options['sink']) && !is_bool($options['sink']) && !is_resource($options['sink']) && !is_string($options['sink']) && !$options['sink'] instanceof StreamInterface) {
            
self::warnInvalidRequestOptionType('sink''resource|string|StreamInterface'$options['sink']);
        }

        
self::warnAboutInvalidTlsFileOptionTypes($options'ssl_key');
        
self::warnIfPresentAndNotString($options'ssl_key_type');
        
self::warnIfPresentAndNotBool($options'stream');
        
self::warnIfPresentAndNotArray($options'stream_context''array<array-key, mixed>');
        
self::warnIfPresentAndNotBool($options'synchronous');
        
self::warnIfPresentAndNotNumber($options'timeout');
        
self::warnIfPresentAndNotBoolOrString($options'verify');
        
self::warnIfPresentAndNotStringOrNumber($options'version');
        
self::warnIfPresentAndNotArray($options'curl''array<int|string, mixed>');

        if (isset(
$options['cookies']) && $options['cookies'] === true) {
            
self::warnInvalidRequestOptionType('cookies''false|CookieJarInterface'$options['cookies']);
        }
    }

    private static function 
warnAboutInvalidAllowRedirectsOptionTypes(array $allowRedirects): void
    
{
        
self::warnIfPresentAndNotInt($allowRedirects'max''allow_redirects.max');
        
self::warnIfPresentAndNotBool($allowRedirects'strict''allow_redirects.strict');
        
self::warnIfPresentAndNotBool($allowRedirects'referer''allow_redirects.referer');
        
self::warnIfPresentAndNotStringArray($allowRedirects'protocols'true'allow_redirects.protocols');
        
self::warnIfPresentAndNotCallable($allowRedirects'on_redirect''allow_redirects.on_redirect');
        
self::warnIfPresentAndNotBool($allowRedirects'track_redirects''allow_redirects.track_redirects');
    }

    
/**
     * @param mixed $auth
     */
    
private static function warnAboutInvalidAuthOptionTypes($auth): void
    
{
        if (
$auth === false || is_string($auth) || $auth === []) {
            return;
        }

        if (!
is_array($auth)) {
            
self::warnInvalidRequestOptionType('auth''array{0: string, 1: string, 2?: string|null}|string|false|null'$auth);

            return;
        }

        if (!
array_key_exists(0$auth) || !is_string($auth[0])) {
            
self::warnInvalidRequestOptionType('auth.0''string'$auth[0] ?? null);
        }

        if (!
array_key_exists(1$auth) || !is_string($auth[1])) {
            
self::warnInvalidRequestOptionType('auth.1''string'$auth[1] ?? null);
        }

        if (
array_key_exists(2$auth) && $auth[2] !== null && !is_string($auth[2])) {
            
self::warnInvalidRequestOptionType('auth.2''string|null'$auth[2]);
        }
    }

    
/**
     * @param mixed $value
     */
    
private static function warnAboutInvalidFormParamTypes($value): void
    
{
        if (!
is_array($value)) {
            
self::warnInvalidRequestOptionType('form_params''array<array-key, string|int|float|bool|null|array>'$value);

            return;
        }

        
self::warnAboutInvalidFormParamArray($value'form_params');
    }

    private static function 
warnAboutInvalidFormParamArray(array $valuesstring $path): bool
    
{
        foreach (
$values as $key => $item) {
            
$itemPath $path.'.'.(string) $key;
            if (
is_array($item)) {
                if (!
self::warnAboutInvalidFormParamArray($item$itemPath)) {
                    return 
false;
                }

                continue;
            }

            if (
$item !== null && !is_scalar($item)) {
                
self::warnInvalidRequestOptionType($itemPath'string|int|float|bool|null|array'$item);

                return 
false;
            }
        }

        return 
true;
    }

    
/**
     * @param mixed $headers
     */
    
private static function warnAboutInvalidHeaderOptionTypes($headers): void
    
{
        if (!
is_array($headers)) {
            
self::warnInvalidRequestOptionType('headers''array<array-key, string|non-empty-array<array-key, string>>|null'$headers);

            return;
        }

        foreach (
$headers as $name => $value) {
            
$path 'headers.'.(string) $name;
            if (
is_array($value)) {
                if (
$value === []) {
                    
self::warnInvalidRequestOptionType($path'string|non-empty-array<array-key, string>'$value);

                    break;
                }

                foreach (
$value as $index => $item) {
                    if (!
is_string($item)) {
                        
self::warnInvalidRequestOptionType($path.'.'.(string) $index'string'$item);

                        break 
2;
                    }
                }
            } elseif (!
is_string($value)) {
                
self::warnInvalidRequestOptionType($path'string|non-empty-array<array-key, string>'$value);

                break;
            }
        }
    }

    
/**
     * @param mixed $multipart
     */
    
private static function warnAboutInvalidMultipartOptionTypes($multipart): void
    
{
        if (!
is_array($multipart)) {
            
self::warnInvalidRequestOptionType('multipart''array<array-key, array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}>'$multipart);

            return;
        }

        foreach (
$multipart as $index => $part) {
            
$path 'multipart.'.(string) $index;
            if (!
is_array($part)) {
                
self::warnInvalidRequestOptionType($path'array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}'$part);

                return;
            }

            if (!
array_key_exists('name'$part) || (!is_string($part['name']) && !is_int($part['name']))) {
                
self::warnInvalidRequestOptionType($path.'.name''string|int'$part['name'] ?? null);
            }

            if (!
array_key_exists('contents'$part)) {
                
self::warnInvalidRequestOptionType($path'array{name: string|int, contents: mixed, headers?: array<array-key, string>, filename?: string}'$part);
            }

            if (
array_key_exists('headers'$part)) {
                if (!
is_array($part['headers'])) {
                    
self::warnInvalidRequestOptionType($path.'.headers''array<array-key, string>'$part['headers']);
                } else {
                    foreach (
$part['headers'] as $name => $value) {
                        if (!
is_string($value)) {
                            
self::warnInvalidRequestOptionType($path.'.headers.'.(string) $name'string'$value);

                            break 
2;
                        }
                    }
                }
            }

            if (
array_key_exists('filename'$part) && !is_string($part['filename'])) {
                
self::warnInvalidRequestOptionType($path.'.filename''string'$part['filename']);
            }
        }
    }

    private static function 
warnAboutInvalidProxyOptionTypes(array $options): void
    
{
        if (!isset(
$options['proxy'])) {
            return;
        }

        if (!
is_string($options['proxy']) && !is_array($options['proxy'])) {
            
self::warnInvalidRequestOptionType('proxy''string|array{http?: string|null, https?: string|null, no?: string|array<array-key, string>|null}'$options['proxy']);

            return;
        }

        if (!
is_array($options['proxy'])) {
            return;
        }

        foreach ([
'http''https'] as $scheme) {
            if (
array_key_exists($scheme$options['proxy']) && $options['proxy'][$scheme] !== null && !is_string($options['proxy'][$scheme])) {
                
self::warnInvalidRequestOptionType('proxy.'.$scheme'string|null'$options['proxy'][$scheme]);
            }
        }

        if (!
array_key_exists('no'$options['proxy']) || $options['proxy']['no'] === null) {
            return;
        }

        if (
is_string($options['proxy']['no'])) {
            return;
        }

        if (!
is_array($options['proxy']['no'])) {
            
self::warnInvalidRequestOptionType('proxy.no''string|array<array-key, string>|null'$options['proxy']['no']);

            return;
        }

        foreach (
$options['proxy']['no'] as $index => $noProxy) {
            if (!
is_string($noProxy)) {
                
self::warnInvalidRequestOptionType('proxy.no.'.(string) $index'string'$noProxy);

                return;
            }
        }
    }

    private static function 
warnAboutInvalidTlsFileOptionTypes(array $optionsstring $option): void
    
{
        if (!isset(
$options[$option])) {
            return;
        }

        if (
is_string($options[$option])) {
            return;
        }

        if (!
is_array($options[$option])) {
            
self::warnInvalidRequestOptionType($option'string|array{0: string, 1?: string}'$options[$option]);

            return;
        }

        if (!
array_key_exists(0$options[$option]) || !is_string($options[$option][0])) {
            
self::warnInvalidRequestOptionType($option.'.0''string'$options[$option][0] ?? null);
        }

        if (
array_key_exists(1$options[$option]) && $options[$option][1] !== null && !is_string($options[$option][1])) {
            
self::warnInvalidRequestOptionType($option.'.1''string|null'$options[$option][1]);
        }
    }

    private static function 
warnIfPresentAndNotArray(array $optionsstring $optionstring $expected): void
    
{
        if (
array_key_exists($option$options) && !is_array($options[$option])) {
            
self::warnInvalidRequestOptionType($option$expected$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotBool(array $optionsstring $option, ?string $path null): void
    
{
        if (
array_key_exists($option$options) && !is_bool($options[$option])) {
            
self::warnInvalidRequestOptionType($path ?? $option'bool'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotBoolOrInt(array $optionsstring $option): void
    
{
        if (
array_key_exists($option$options) && !is_bool($options[$option]) && !is_int($options[$option])) {
            
self::warnInvalidRequestOptionType($option'bool|int'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotBoolOrResource(array $optionsstring $option): void
    
{
        if (
array_key_exists($option$options) && !is_bool($options[$option]) && !is_resource($options[$option])) {
            
self::warnInvalidRequestOptionType($option'bool|resource'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotBoolOrString(array $optionsstring $option): void
    
{
        if (
array_key_exists($option$options) && !is_bool($options[$option]) && !is_string($options[$option])) {
            
self::warnInvalidRequestOptionType($option'bool|string'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotCallable(array $optionsstring $option, ?string $path null): void
    
{
        if (
array_key_exists($option$options) && !is_callable($options[$option])) {
            
self::warnInvalidRequestOptionType($path ?? $option'callable'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotInt(array $optionsstring $option, ?string $path null): void
    
{
        if (
array_key_exists($option$options) && !is_int($options[$option])) {
            
self::warnInvalidRequestOptionType($path ?? $option'int'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotNumber(array $optionsstring $option): void
    
{
        if (
array_key_exists($option$options) && !is_int($options[$option]) && !is_float($options[$option])) {
            
self::warnInvalidRequestOptionType($option'int|float'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotString(array $optionsstring $option): void
    
{
        if (
array_key_exists($option$options) && !is_string($options[$option])) {
            
self::warnInvalidRequestOptionType($option'string'$options[$option]);
        }
    }

    private static function 
warnIfPresentAndNotStringArray(array $optionsstring $optionbool $nonEmpty, ?string $path null): void
    
{
        if (!
array_key_exists($option$options)) {
            return;
        }

        
$path $path ?? $option;
        
$expected = ($nonEmpty 'non-empty-' '').'array<array-key, string>';

        if (!
is_array($options[$option]) || ($nonEmpty && $options[$option] === [])) {
            
self::warnInvalidRequestOptionType($path$expected$options[$option]);

            return;
        }

        foreach (
$options[$option] as $index => $item) {
            if (!
is_string($item)) {
                
self::warnInvalidRequestOptionType($path.'.'.(string) $index'string'$item);

                return;
            }
        }
    }

    private static function 
warnIfPresentAndNotStringOrNumber(array $optionsstring $option): void
    
{
        if (
            
array_key_exists($option$options)
            && !
is_string($options[$option])
            && !
is_int($options[$option])
            && !
is_float($options[$option])
        ) {
            
self::warnInvalidRequestOptionType($option'string|int|float'$options[$option]);
        }
    }

    
/**
     * @param mixed $value
     */
    
private static function warnInvalidRequestOptionType(string $optionstring $expected$value): void
    
{
        
trigger_deprecation(
            
'guzzlehttp/guzzle',
            
'7.11',
            
'Passing %s to request option "%s" is deprecated; guzzlehttp/guzzle 8.0 requires %s.',
            
get_debug_type($value),
            
$option,
            
$expected
        
);
    }

    
/**
     * Transfers the given request and applies request options.
     *
     * The URI of the request is not modified and the request options are used
     * as-is without merging in default options.
     *
     * @param array $options See {@see RequestOptions}.
     */
    
private function transfer(RequestInterface $request, array $options): PromiseInterface
    
{
        
$request $this->applyOptions($request$options);

        
$protocolVersion $request->getProtocolVersion();

        if (
'' === $protocolVersion) {
            
trigger_deprecation('guzzlehttp/guzzle''7.11''Sending a request with an empty protocol version is deprecated; guzzlehttp/guzzle 8.0 will reject empty protocol versions.');

            
$request Psr7Utils::modifyRequest($request, ['version' => '1.1']);
        } elseif (!
self::isProtocolVersionValid($protocolVersion)) {
            
trigger_deprecation('guzzlehttp/guzzle''7.11''Sending a request with a malformed protocol version is deprecated; guzzlehttp/guzzle 8.0 will reject malformed protocol versions.');
        }

        
/** @var HandlerStack $handler */
        
$handler $options['handler'];

        try {
            return 
PCreate::promiseFor($handler($request$options));
        } catch (
Exception $e) {
            return 
PCreate::rejectionFor($e);
        }
    }

    
/**
     * Applies the array of request options to a request.
     */
    
private function applyOptions(RequestInterface $request, array &$options): RequestInterface
    
{
        
$modify = [
            
'set_headers' => [],
        ];

        if (isset(
$options['headers'])) {
            if (
array_keys($options['headers']) === range(0count($options['headers']) - 1)) {
                throw new 
InvalidArgumentException('The headers array must have header name as keys.');
            }
            
$headers $options['headers'];
            
$droppedHeaderNames self::castDeprecatedHeaderOptionValues($headers);
            if (
$droppedHeaderNames !== [] && isset($options['_conditional'])) {
                
$options['_conditional'] = Psr7Utils::caselessRemove($droppedHeaderNames$options['_conditional']);
            }
            
$modify['set_headers'] = $headers;
            unset(
$options['headers']);
        }

        if (isset(
$options['form_params'])) {
            if (isset(
$options['multipart'])) {
                throw new 
InvalidArgumentException('You cannot use '
                    
.'form_params and multipart at the same time. Use the '
                    
.'form_params option if you want to send application/'
                    
.'x-www-form-urlencoded requests, and the multipart '
                    
.'option to send multipart/form-data requests.');
            }
            
$options['body'] = http_build_query(self::normalizeNonFiniteFloats($options['form_params'], 'form_params'), '''&');
            unset(
$options['form_params']);
            
// Ensure that we don't have the header in different case and set the new value.
            
$options['_conditional'] = Psr7Utils::caselessRemove(['Content-Type'], $options['_conditional']);
            
$options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
        }

        if (isset(
$options['multipart'])) {
            
$options['body'] = new Psr7MultipartStream($options['multipart']);
            unset(
$options['multipart']);
        }

        if (isset(
$options['json'])) {
            
$options['body'] = Utils::jsonEncode($options['json']);
            unset(
$options['json']);
            
// Ensure that we don't have the header in different case and set the new value.
            
$options['_conditional'] = Psr7Utils::caselessRemove(['Content-Type'], $options['_conditional']);
            
$options['_conditional']['Content-Type'] = 'application/json';
        }

        if (!empty(
$options['decode_content'])
            && 
$options['decode_content'] !== true
        
) {
            
// Ensure that we don't have the header in different case and set the new value.
            
$options['_conditional'] = Psr7Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
            
$modify['set_headers']['Accept-Encoding'] = (string) $options['decode_content'];
        }

        if (isset(
$options['body'])) {
            if (
is_array($options['body'])) {
                throw 
$this->invalidBody();
            }
            
$modify['body'] = self::createBodyStream($options['body']);
            unset(
$options['body']);
        }

        if (!empty(
$options['auth']) && is_array($options['auth'])) {
            
$value $options['auth'];
            
$type = isset($value[2]) ? strtolower($value[2]) : 'basic';
            switch (
$type) {
                case 
'basic':
                    
// Ensure that we don't have the header in different case and set the new value.
                    
$modify['set_headers'] = Psr7Utils::caselessRemove(['Authorization'], $modify['set_headers']);
                    
$modify['set_headers']['Authorization'] = 'Basic '
                        
.base64_encode("$value[0]:$value[1]");
                    break;
                case 
'digest':
                    
// @todo: Do not rely on curl
                    
$options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
                    
$options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
                    break;
                case 
'ntlm':
                    
trigger_deprecation(
                        
'guzzlehttp/guzzle',
                        
'7.12',
                        
'Passing "ntlm" as the built-in auth type is deprecated; guzzlehttp/guzzle 8.0 will no longer apply NTLM through the "auth" request option. Configure NTLM with cURL HTTP authentication options instead.'
                    
);
                    
$options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
                    
$options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
                    break;
            }
        }

        if (isset(
$options['query'])) {
            
$value $options['query'];
            if (
is_array($value)) {
                
$value http_build_query(self::normalizeNonFiniteFloats($value'query'), '''&'PHP_QUERY_RFC3986);
            }
            if (!
is_string($value)) {
                throw new 
InvalidArgumentException('query must be a string or array');
            }
            
$modify['query'] = $value;
            unset(
$options['query']);
        }

        
// Ensure that sink is not an invalid value.
        
if (isset($options['sink'])) {
            
// TODO: Add more sink validation?
            
if (is_bool($options['sink'])) {
                throw new 
InvalidArgumentException('sink must not be a boolean');
            }
        }

        if (isset(
$options['version'])) {
            
$modify['version'] = self::normalizeProtocolVersion($options['version']);
        }

        
$request Psr7Utils::modifyRequest($request$modify);
        if (
$request->getBody() instanceof Psr7MultipartStream) {
            
// Use a multipart/form-data POST if a Content-Type is not set.
            // Ensure that we don't have the header in different case and set the new value.
            
$options['_conditional'] = Psr7Utils::caselessRemove(['Content-Type'], $options['_conditional']);
            
$options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
                
.$request->getBody()->getBoundary();
        }

        
// Merge in conditional headers if they are not present.
        
if (isset($options['_conditional'])) {
            
// Build up the changes so it's in a single clone of the message.
            
$modify = [];
            foreach (
$options['_conditional'] as $k => $v) {
                
$name = (string) $k;
                if (!
$request->hasHeader($name)) {
                    
$modify['set_headers'][$name] = $v;
                }
            }
            
$request Psr7Utils::modifyRequest($request$modify);
            
// Don't pass this internal value along to middleware/handlers.
            
unset($options['_conditional']);
        }

        return 
$request;
    }

    
/**
     * @param array<array-key, mixed> $headers
     *
     * @return list<string>
     */
    
private static function castDeprecatedHeaderOptionValues(array &$headers): array
    {
        
$droppedHeaderNames = [];

        foreach (
$headers as $name => $value) {
            if (
is_array($value)) {
                if (
$value === []) {
                    
$droppedHeaderNames[] = (string) $name;
                    unset(
$headers[$name]);

                    continue;
                }

                foreach (
$value as $index => $item) {
                    if (
$item === null || (!is_string($item) && is_scalar($item))) {
                        if (
is_float($item) && !is_finite($item)) {
                            
$item is_nan($item) ? 'NAN' : ($item 'INF' '-INF');
                        }
                        
$value[$index] = (string) $item;
                    }
                }

                
$headers[$name] = $value;

                continue;
            }

            if (
$value === null || (!is_string($value) && is_scalar($value))) {
                if (
is_float($value) && !is_finite($value)) {
                    
$value is_nan($value) ? 'NAN' : ($value 'INF' '-INF');
                }
                
$headers[$name] = (string) $value;
            }
        }

        return 
$droppedHeaderNames;
    }

    
/**
     * @param mixed $body
     */
    
private static function createBodyStream($body): StreamInterface
    
{
        if (
$body instanceof StreamInterface) {
            return 
$body;
        }

        if (
is_resource($body) || $body === null || is_string($body) || $body instanceof Iterator) {
            return 
Psr7Utils::streamFor($body);
        }

        if (
is_scalar($body)) {
            
trigger_deprecation('guzzlehttp/guzzle''7.12''Passing a non-string scalar to the "body" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-string scalar bodies.');

            return 
Psr7Utils::streamFor(self::stringifyScalar($body));
        }

        if (
is_object($body) && method_exists($body'__toString')) {
            return 
Psr7Utils::streamFor((string) $body);
        }

        if (
is_callable($body)) {
            return 
Psr7Utils::streamFor($body);
        }

        throw new 
InvalidArgumentException(sprintf(
            
'Passing %s to request option "body" is invalid; expected resource|string|null|int|float|bool|StreamInterface|callable&object|Iterator|Stringable.',
            
get_debug_type($body)
        ));
    }

    
/**
     * @param bool|float|int|string $value
     */
    
private static function stringifyScalar($value): string
    
{
        
// Normalize non-finite floats to dodge PHP 8.5's (string) NAN
        // coercion warning while the value is still accepted.
        
if (is_float($value) && !is_finite($value)) {
            
$value is_nan($value) ? 'NAN' : ($value 'INF' '-INF');
        }

        return (string) 
$value;
    }

    
/**
     * Converts non-finite floats in the array to the strings PHP coerces
     * them to, as implicit coercion of NAN emits a warning on PHP 8.5.
     */
    
private static function normalizeNonFiniteFloats(array $valuesstring $option): array
    {
        foreach (
$values as $key => $value) {
            if (
is_array($value)) {
                
$values[$key] = self::normalizeNonFiniteFloats($value$option);
            } elseif (
is_float($value) && !is_finite($value)) {
                
trigger_deprecation('guzzlehttp/guzzle''7.12''Passing a non-finite float in the "%s" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-finite floats.'$option);

                
$values[$key] = is_nan($value) ? 'NAN' : ($value 'INF' '-INF');
            }
        }

        return 
$values;
    }

    
/**
     * @param string|int|float $version
     */
    
private static function normalizeProtocolVersion($version): string
    
{
        if (
'' === $version) {
            
trigger_deprecation('guzzlehttp/guzzle''7.11''Passing an empty "version" request option is deprecated; guzzlehttp/guzzle 8.0 will reject empty protocol versions.');

            return 
'1.1';
        }

        return 
is_float($version) ? number_format($version1'.''') : (string) $version;
    }

    private static function 
isProtocolVersionValid(string $version): bool
    
{
        return 
=== preg_match('/^d+(?:.d+)?$/D'$version);
    }

    
/**
     * Return an InvalidArgumentException with pre-set message.
     */
    
private function invalidBody(): InvalidArgumentException
    
{
        return new 
InvalidArgumentException('Passing in the "body" request '
            
.'option as an array to send a request is not supported. '
            
.'Please use the "form_params" request option to send a '
            
.'application/x-www-form-urlencoded request, or the "multipart" '
            
.'request option to send a multipart/form-data request.');
    }
}
Онлайн: 2
Реклама