vendor/aws/aws-sdk-php/src/ClientResolver.php line 373

Open in your IDE?
  1. <?php
  2. namespace Aws;
  3. use Aws\Api\Validator;
  4. use Aws\Api\ApiProvider;
  5. use Aws\Api\Service;
  6. use Aws\Credentials\Credentials;
  7. use Aws\Credentials\CredentialsInterface;
  8. use Aws\Signature\SignatureProvider;
  9. use Aws\Endpoint\EndpointProvider;
  10. use Aws\Credentials\CredentialProvider;
  11. use GuzzleHttp\Promise;
  12. use InvalidArgumentException as IAE;
  13. /**
  14.  * @internal Resolves a hash of client arguments to construct a client.
  15.  */
  16. class ClientResolver
  17. {
  18.     /** @var array */
  19.     private $argDefinitions;
  20.     /** @var array Map of types to a corresponding function */
  21.     private static $typeMap = [
  22.         'resource' => 'is_resource',
  23.         'callable' => 'is_callable',
  24.         'int'      => 'is_int',
  25.         'bool'     => 'is_bool',
  26.         'string'   => 'is_string',
  27.         'object'   => 'is_object',
  28.         'array'    => 'is_array',
  29.     ];
  30.     private static $defaultArgs = [
  31.         'service' => [
  32.             'type'     => 'value',
  33.             'valid'    => ['string'],
  34.             'doc'      => 'Name of the service to utilize. This value will be supplied by default when using one of the SDK clients (e.g., Aws\\S3\\S3Client).',
  35.             'required' => true,
  36.             'internal' => true
  37.         ],
  38.         'exception_class' => [
  39.             'type'     => 'value',
  40.             'valid'    => ['string'],
  41.             'doc'      => 'Exception class to create when an error occurs.',
  42.             'default'  => 'Aws\Exception\AwsException',
  43.             'internal' => true
  44.         ],
  45.         'scheme' => [
  46.             'type'     => 'value',
  47.             'valid'    => ['string'],
  48.             'default'  => 'https',
  49.             'doc'      => 'URI scheme to use when connecting connect. The SDK will utilize "https" endpoints (i.e., utilize SSL/TLS connections) by default. You can attempt to connect to a service over an unencrypted "http" endpoint by setting ``scheme`` to "http".',
  50.         ],
  51.         'endpoint' => [
  52.             'type'  => 'value',
  53.             'valid' => ['string'],
  54.             'doc'   => 'The full URI of the webservice. This is only required when connecting to a custom endpoint (e.g., a local version of S3).',
  55.         ],
  56.         'region' => [
  57.             'type'     => 'value',
  58.             'valid'    => ['string'],
  59.             'required' => [__CLASS__'_missing_region'],
  60.             'doc'      => 'Region to connect to. See http://docs.aws.amazon.com/general/latest/gr/rande.html for a list of available regions.',
  61.         ],
  62.         'version' => [
  63.             'type'     => 'value',
  64.             'valid'    => ['string'],
  65.             'required' => [__CLASS__'_missing_version'],
  66.             'doc'      => 'The version of the webservice to utilize (e.g., 2006-03-01).',
  67.         ],
  68.         'signature_provider' => [
  69.             'type'    => 'value',
  70.             'valid'   => ['callable'],
  71.             'doc'     => 'A callable that accepts a signature version name (e.g., "v4"), a service name, and region, and  returns a SignatureInterface object or null. This provider is used to create signers utilized by the client. See Aws\\Signature\\SignatureProvider for a list of built-in providers',
  72.             'default' => [__CLASS__'_default_signature_provider'],
  73.         ],
  74.         'endpoint_provider' => [
  75.             'type'     => 'value',
  76.             'valid'    => ['callable'],
  77.             'fn'       => [__CLASS__'_apply_endpoint_provider'],
  78.             'doc'      => 'An optional PHP callable that accepts a hash of options including a "service" and "region" key and returns NULL or a hash of endpoint data, of which the "endpoint" key is required. See Aws\\Endpoint\\EndpointProvider for a list of built-in providers.',
  79.             'default' => [__CLASS__'_default_endpoint_provider'],
  80.         ],
  81.         'api_provider' => [
  82.             'type'     => 'value',
  83.             'valid'    => ['callable'],
  84.             'doc'      => 'An optional PHP callable that accepts a type, service, and version argument, and returns an array of corresponding configuration data. The type value can be one of api, waiter, or paginator.',
  85.             'fn'       => [__CLASS__'_apply_api_provider'],
  86.             'default'  => [ApiProvider::class, 'defaultProvider'],
  87.         ],
  88.         'signature_version' => [
  89.             'type'    => 'config',
  90.             'valid'   => ['string'],
  91.             'doc'     => 'A string representing a custom signature version to use with a service (e.g., v4). Note that per/operation signature version MAY override this requested signature version.',
  92.             'default' => [__CLASS__'_default_signature_version'],
  93.         ],
  94.         'profile' => [
  95.             'type'  => 'config',
  96.             'valid' => ['string'],
  97.             'doc'   => 'Allows you to specify which profile to use when credentials are created from the AWS credentials file in your HOME directory. This setting overrides the AWS_PROFILE environment variable. Note: Specifying "profile" will cause the "credentials" key to be ignored.',
  98.             'fn'    => [__CLASS__'_apply_profile'],
  99.         ],
  100.         'credentials' => [
  101.             'type'    => 'value',
  102.             'valid'   => ['Aws\Credentials\CredentialsInterface''array''bool''callable'],
  103.             'doc'     => 'Specifies the credentials used to sign requests. Provide an Aws\Credentials\CredentialsInterface object, an associative array of "key", "secret", and an optional "token" key, `false` to use null credentials, or a callable credentials provider used to create credentials or return null. See Aws\\Credentials\\CredentialProvider for a list of built-in credentials providers. If no credentials are provided, the SDK will attempt to load them from the environment.',
  104.             'fn'      => [__CLASS__'_apply_credentials'],
  105.             'default' => [CredentialProvider::class, 'defaultProvider'],
  106.         ],
  107.         'retries' => [
  108.             'type'    => 'value',
  109.             'valid'   => ['int'],
  110.             'doc'     => 'Configures the maximum number of allowed retries for a client (pass 0 to disable retries). ',
  111.             'fn'      => [__CLASS__'_apply_retries'],
  112.             'default' => 3,
  113.         ],
  114.         'validate' => [
  115.             'type'    => 'value',
  116.             'valid'   => ['bool'],
  117.             'default' => true,
  118.             'doc'     => 'Set to false to disable client-side parameter validation.',
  119.             'fn'      => [__CLASS__'_apply_validate'],
  120.         ],
  121.         'debug' => [
  122.             'type'  => 'value',
  123.             'valid' => ['bool''array'],
  124.             'doc'   => 'Set to true to display debug information when sending requests. Alternatively, you can provide an associative array with the following keys: logfn: (callable) Function that is invoked with log messages; stream_size: (int) When the size of a stream is greater than this number, the stream data will not be logged (set to "0" to not log any stream data); scrub_auth: (bool) Set to false to disable the scrubbing of auth data from the logged messages; http: (bool) Set to false to disable the "debug" feature of lower level HTTP adapters (e.g., verbose curl output).',
  125.             'fn'    => [__CLASS__'_apply_debug'],
  126.         ],
  127.         'http' => [
  128.             'type'    => 'value',
  129.             'valid'   => ['array'],
  130.             'default' => [],
  131.             'doc'     => 'Set to an array of SDK request options to apply to each request (e.g., proxy, verify, etc.).',
  132.         ],
  133.         'http_handler' => [
  134.             'type'    => 'value',
  135.             'valid'   => ['callable'],
  136.             'doc'     => 'An HTTP handler is a function that accepts a PSR-7 request object and returns a promise that is fulfilled with a PSR-7 response object or rejected with an array of exception data. NOTE: This option supersedes any provided "handler" option.',
  137.             'fn'      => [__CLASS__'_apply_http_handler']
  138.         ],
  139.         'handler' => [
  140.             'type'     => 'value',
  141.             'valid'    => ['callable'],
  142.             'doc'      => 'A handler that accepts a command object, request object and returns a promise that is fulfilled with an Aws\ResultInterface object or rejected with an Aws\Exception\AwsException. A handler does not accept a next handler as it is terminal and expected to fulfill a command. If no handler is provided, a default Guzzle handler will be utilized.',
  143.             'fn'       => [__CLASS__'_apply_handler'],
  144.             'default'  => [__CLASS__'_default_handler']
  145.         ]
  146.     ];
  147.     /**
  148.      * Gets an array of default client arguments, each argument containing a
  149.      * hash of the following:
  150.      *
  151.      * - type: (string, required) option type described as follows:
  152.      *   - value: The default option type.
  153.      *   - config: The provided value is made available in the client's
  154.      *     getConfig() method.
  155.      * - valid: (array, required) Valid PHP types or class names. Note: null
  156.      *   is not an allowed type.
  157.      * - required: (bool, callable) Whether or not the argument is required.
  158.      *   Provide a function that accepts an array of arguments and returns a
  159.      *   string to provide a custom error message.
  160.      * - default: (mixed) The default value of the argument if not provided. If
  161.      *   a function is provided, then it will be invoked to provide a default
  162.      *   value. The function is provided the array of options and is expected
  163.      *   to return the default value of the option.
  164.      * - doc: (string) The argument documentation string.
  165.      * - fn: (callable) Function used to apply the argument. The function
  166.      *   accepts the provided value, array of arguments by reference, and an
  167.      *   event emitter.
  168.      *
  169.      * Note: Order is honored and important when applying arguments.
  170.      *
  171.      * @return array
  172.      */
  173.     public static function getDefaultArguments()
  174.     {
  175.         return self::$defaultArgs;
  176.     }
  177.     /**
  178.      * @param array $argDefinitions Client arguments.
  179.      */
  180.     public function __construct(array $argDefinitions)
  181.     {
  182.         $this->argDefinitions $argDefinitions;
  183.     }
  184.     /**
  185.      * Resolves client configuration options and attached event listeners.
  186.      *
  187.      * @param array       $args Provided constructor arguments.
  188.      * @param HandlerList $list Handler list to augment.
  189.      *
  190.      * @return array Returns the array of provided options.
  191.      * @throws \InvalidArgumentException
  192.      * @see Aws\AwsClient::__construct for a list of available options.
  193.      */
  194.     public function resolve(array $argsHandlerList $list)
  195.     {
  196.         $args['config'] = [];
  197.         foreach ($this->argDefinitions as $key => $a) {
  198.             // Add defaults, validate required values, and skip if not set.
  199.             if (!isset($args[$key])) {
  200.                 if (isset($a['default'])) {
  201.                     // Merge defaults in when not present.
  202.                     $args[$key] = is_callable($a['default'])
  203.                         ? $a['default']($args)
  204.                         : $a['default'];
  205.                 } elseif (empty($a['required'])) {
  206.                     continue;
  207.                 } else {
  208.                     $this->throwRequired($args);
  209.                 }
  210.             }
  211.             // Validate the types against the provided value.
  212.             foreach ($a['valid'] as $check) {
  213.                 if (isset(self::$typeMap[$check])) {
  214.                     $fn self::$typeMap[$check];
  215.                     if ($fn($args[$key])) {
  216.                         goto is_valid;
  217.                     }
  218.                 } elseif ($args[$key] instanceof $check) {
  219.                     goto is_valid;
  220.                 }
  221.             }
  222.             $this->invalidType($key$args[$key]);
  223.             // Apply the value
  224.             is_valid:
  225.             if (isset($a['fn'])) {
  226.                 $a['fn']($args[$key], $args$list);
  227.             }
  228.             if ($a['type'] === 'config') {
  229.                 $args['config'][$key] = $args[$key];
  230.             }
  231.         }
  232.         return $args;
  233.     }
  234.     /**
  235.      * Creates a verbose error message for an invalid argument.
  236.      *
  237.      * @param string $name        Name of the argument that is missing.
  238.      * @param array  $args        Provided arguments
  239.      * @param bool   $useRequired Set to true to show the required fn text if
  240.      *                            available instead of the documentation.
  241.      * @return string
  242.      */
  243.     private function getArgMessage($name$args = [], $useRequired false)
  244.     {
  245.         $arg $this->argDefinitions[$name];
  246.         $msg '';
  247.         $modifiers = [];
  248.         if (isset($arg['valid'])) {
  249.             $modifiers[] = implode('|'$arg['valid']);
  250.         }
  251.         if (isset($arg['choice'])) {
  252.             $modifiers[] = 'One of ' implode(', '$arg['choice']);
  253.         }
  254.         if ($modifiers) {
  255.             $msg .= '(' implode('; '$modifiers) . ')';
  256.         }
  257.         $msg wordwrap("{$name}{$msg}"75"\n  ");
  258.         if ($useRequired && is_callable($arg['required'])) {
  259.             $msg .= "\n\n  ";
  260.             $msg .= str_replace("\n""\n  "call_user_func($arg['required'], $args));
  261.         } elseif (isset($arg['doc'])) {
  262.             $msg .= wordwrap("\n\n  {$arg['doc']}"75"\n  ");
  263.         }
  264.         return $msg;
  265.     }
  266.     /**
  267.      * Throw when an invalid type is encountered.
  268.      *
  269.      * @param string $name     Name of the value being validated.
  270.      * @param mixed  $provided The provided value.
  271.      * @throws \InvalidArgumentException
  272.      */
  273.     private function invalidType($name$provided)
  274.     {
  275.         $expected implode('|'$this->argDefinitions[$name]['valid']);
  276.         $msg "Invalid configuration value "
  277.             "provided for \"{$name}\". Expected {$expected}, but got "
  278.             describe_type($provided) . "\n\n"
  279.             $this->getArgMessage($name);
  280.         throw new \InvalidArgumentException($msg);
  281.     }
  282.     /**
  283.      * Throws an exception for missing required arguments.
  284.      *
  285.      * @param array $args Passed in arguments.
  286.      * @throws \InvalidArgumentException
  287.      */
  288.     private function throwRequired(array $args)
  289.     {
  290.         $missing = [];
  291.         foreach ($this->argDefinitions as $k => $a) {
  292.             if (empty($a['required'])
  293.                 || isset($a['default'])
  294.                 || array_key_exists($k$args)
  295.             ) {
  296.                 continue;
  297.             }
  298.             $missing[] = $this->getArgMessage($k$argstrue);
  299.         }
  300.         $msg "Missing required client configuration options: \n\n";
  301.         $msg .= implode("\n\n"$missing);
  302.         throw new IAE($msg);
  303.     }
  304.     public static function _apply_retries($value, array &$argsHandlerList $list)
  305.     {
  306.         if ($value) {
  307.             $decider RetryMiddleware::createDefaultDecider($value);
  308.             $list->appendSign(Middleware::retry($decider), 'retry');
  309.         }
  310.     }
  311.     public static function _apply_credentials($value, array &$args)
  312.     {
  313.         if (is_callable($value)) {
  314.             return;
  315.         } elseif ($value instanceof CredentialsInterface) {
  316.             $args['credentials'] = CredentialProvider::fromCredentials($value);
  317.         } elseif (is_array($value)
  318.             && isset($value['key'])
  319.             && isset($value['secret'])
  320.         ) {
  321.             $args['credentials'] = CredentialProvider::fromCredentials(
  322.                 new Credentials(
  323.                     $value['key'],
  324.                     $value['secret'],
  325.                     isset($value['token']) ? $value['token'] : null,
  326.                     isset($value['expires']) ? $value['expires'] : null
  327.                 )
  328.             );
  329.         } elseif ($value === false) {
  330.             $args['credentials'] = CredentialProvider::fromCredentials(
  331.                 new Credentials('''')
  332.             );
  333.             $args['config']['signature_version'] = 'anonymous';
  334.         } else {
  335.             throw new IAE('Credentials must be an instance of '
  336.                 'Aws\Credentials\CredentialsInterface, an associative '
  337.                 'array that contains "key", "secret", and an optional "token" '
  338.                 'key-value pairs, a credentials provider function, or false.');
  339.         }
  340.     }
  341.     public static function _apply_api_provider(callable $value, array &$argsHandlerList $list)
  342.     {
  343.         $api = new Service(
  344.             ApiProvider::resolve(
  345.                 $value,
  346.                 'api',
  347.                 $args['service'],
  348.                 $args['version']
  349.             ),
  350.             $value
  351.         );
  352.         $args['api'] = $api;
  353.         $args['serializer'] = Service::createSerializer($api$args['endpoint']);
  354.         $args['parser'] = Service::createParser($api);
  355.         $args['error_parser'] = Service::createErrorParser($api->getProtocol());
  356.         $list->prependBuild(Middleware::requestBuilder($args['serializer']), 'builder');
  357.     }
  358.     public static function _apply_endpoint_provider(callable $value, array &$args)
  359.     {
  360.         if (!isset($args['endpoint'])) {
  361.             // Invoke the endpoint provider and throw if it does not resolve.
  362.             $result EndpointProvider::resolve($value, [
  363.                 'service' => $args['service'],
  364.                 'region'  => $args['region'],
  365.                 'scheme'  => $args['scheme']
  366.             ]);
  367.             $args['endpoint'] = $result['endpoint'];
  368.             if (isset($result['signatureVersion'])) {
  369.                 $args['config']['signature_version'] = $result['signatureVersion'];
  370.             }
  371.         }
  372.     }
  373.     public static function _apply_debug($value, array &$argsHandlerList $list)
  374.     {
  375.         if ($value !== false) {
  376.             $list->interpose(new TraceMiddleware($value === true ? [] : $value));
  377.         }
  378.     }
  379.     public static function _apply_profile($_, array &$args)
  380.     {
  381.         $args['credentials'] = CredentialProvider::ini($args['profile']);
  382.     }
  383.     public static function _apply_validate($value, array &$argsHandlerList $list)
  384.     {
  385.         if ($value === true) {
  386.             $list->appendValidate(
  387.                 Middleware::validation($args['api'], new Validator()),
  388.                 'validation'
  389.             );
  390.         }
  391.     }
  392.     public static function _apply_handler($value, array &$argsHandlerList $list)
  393.     {
  394.         $list->setHandler($value);
  395.     }
  396.     public static function _default_handler(array &$args)
  397.     {
  398.         return new WrappedHttpHandler(
  399.             default_http_handler(),
  400.             $args['parser'],
  401.             $args['error_parser'],
  402.             $args['exception_class']
  403.         );
  404.     }
  405.     public static function _apply_http_handler($value, array &$argsHandlerList $list)
  406.     {
  407.         $args['handler'] = new WrappedHttpHandler(
  408.             $value,
  409.             $args['parser'],
  410.             $args['error_parser'],
  411.             $args['exception_class']
  412.         );
  413.     }
  414.     public static function _default_endpoint_provider()
  415.     {
  416.         return EndpointProvider::defaultProvider();
  417.     }
  418.     public static function _default_signature_provider()
  419.     {
  420.         return SignatureProvider::defaultProvider();
  421.     }
  422.     public static function _default_signature_version(array &$args)
  423.     {
  424.         return isset($args['config']['signature_version'])
  425.             ? $args['config']['signature_version']
  426.             : $args['api']->getSignatureVersion();
  427.     }
  428.     public static function _missing_version(array $args)
  429.     {
  430.         $service = isset($args['service']) ? $args['service'] : '';
  431.         $versions ApiProvider::defaultProvider()->getVersions($service);
  432.         $versions implode("\n"array_map(function ($v) {
  433.             return "* \"$v\"";
  434.         }, $versions)) ?: '* (none found)';
  435.         return <<<EOT
  436. A "version" configuration value is required. Specifying a version constraint
  437. ensures that your code will not be affected by a breaking change made to the
  438. service. For example, when using Amazon S3, you can lock your API version to
  439. "2006-03-01".
  440. Your build of the SDK has the following version(s) of "{$service}": {$versions}
  441. You may provide "latest" to the "version" configuration value to utilize the
  442. most recent available API version that your client's API provider can find.
  443. Note: Using 'latest' in a production application is not recommended.
  444. A list of available API versions can be found on each client's API documentation
  445. page: http://docs.aws.amazon.com/aws-sdk-php/v3/api/index.html. If you are
  446. unable to load a specific API version, then you may need to update your copy of
  447. the SDK.
  448. EOT;
  449.     }
  450.     public static function _missing_region(array $args)
  451.     {
  452.         $service = isset($args['service']) ? $args['service'] : '';
  453.         return <<<EOT
  454. A "region" configuration value is required for the "{$service}" service
  455. (e.g., "us-west-2"). A list of available public regions and endpoints can be
  456. found at http://docs.aws.amazon.com/general/latest/gr/rande.html.
  457. EOT;
  458.     }
  459. }